Jack Sullivan Jack Sullivan - 3 months ago 14
C++ Question

Template Metaprogramming: "does not have integral or enumeration type"

I'm trying to write a "power" function using templates.

#define VAR_X 2.0f

template <int N> struct Power
{
static const float ret = VAR_X * Power<N-1>::ret;
};

template <> struct Power<0>
{
static const float ret = 1.0f;
};


I used a macro for the variable because floating point numbers can't be used as template parameters. When I try to compile with g++ 5.4.0, I get this:

tmptrig.cpp: In instantiation of ‘const float Power<1>::ret’:
tmptrig.cpp:35:28: required from here
tmptrig.cpp:17:24: error: the value of ‘Power<0>::ret’ is not usable in a constant expression
static const float ret = VAR_X * Power<N-1>::ret;
^
tmptrig.cpp:22:24: note: ‘Power<0>::ret’ does not have integral or enumeration type
static const float ret = 1.0f;


However, when I change the program to only deal with integers, it works fine.

#define VAR_X 2

template <int N> struct Power
{
static const int ret = VAR_X * Power<N-1>::ret;
};

template <> struct Power<0>
{
static const int ret = 1;
};


I know that floating point numbers cannot be used as template parameters, but (as far as I know) that's not how they're being treated here. Why doesn't the compiler like it when I use floats?

Edit: this is what my
main
looks like:

int main(int argc, char *argv[])
{
std::cout << Power<1>::ret << std::endl;
}

Answer Source

The problem is not that floating point numbers cannot be constant expressions, because they can (despite not being admissible as template non-type parameters). The problem is that your code needs to mark them a such explicitly, or they cannot be initialized in the class defintion itself.

#include <iostream>

#define VAR_X 2.0f

template <int N> struct Power
{
    static constexpr float ret = VAR_X * Power<N-1>::ret;
};

template <> struct Power<0>
{
    static constexpr float ret = 1.0f;
};

int main(int argc, char *argv[])
{
    std::cout << Power<1>::ret << std::endl;
}

It's pretty much the same as your original intent, since constexpr implies const.

Clang gives a more helpful warning about your original code:

error: in-class initializer for static data member of type 'const float' requires 'constexpr' specifier [-Wstatic-float-init]
    static const float ret = VAR_X * Power<N-1>::ret;

They can even be made constant expressions (and the results of meta-functions) in C++03, but that requires an out of class definition:

template <int N> struct Power
{
    static const float ret;
};

template <int N>
const float Power<N>::ret = VAR_X * Power<N-1>::ret;

template <> struct Power<0>
{
    static const float ret;
};

const float Power<0>::ret = 1.0f;