1

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;
}

2 Answers2

1

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>

float constexpr 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:

float const var_x = 2.0f;

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;
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Please remove that nonsense macro from your answer. It makes me weep. :'-( – Konrad Rudolph Jul 02 '17 at 14:23
  • I don't think C++03 constant expressions can contain variables of floating point types. (It seems that even in C++11 and later, non-constexpr floating point variables are still not allowed in a constant expression.) – cpplearner Jul 02 '17 at 14:35
  • @KonradRudolph - Done. Must admit I didn't really notice the macro. I blame tunnel vision. – StoryTeller - Unslander Monica Jul 03 '17 at 05:42
  • 1
    @cpplearner - Const variables of any fundamental type were always constant expressions in C++, so long as their address wasn't taken. They had to be, or any suggestion to replace macros by consts (in headers too, no less) wouldn't hold any water. – StoryTeller - Unslander Monica Jul 03 '17 at 05:43
  • Well [`const float x = 3.f; int a[(int)x];` does not compile.](https://wandbox.org/permlink/X8kpYDa8ymDpQtdI) (And FWIW, [`int a [(int)+3.f];` does not compile with `-std=c++03`](https://wandbox.org/permlink/jEkLfZqGiLhrp3EL).) – cpplearner Jul 03 '17 at 06:25
  • 1
    @cpplearner - And? Once you cast it isn't a constant expression anymore (you can look it up). Or are claiming that this proves `3.0f` isn't a constant expression either? Because it is by definition. – StoryTeller - Unslander Monica Jul 03 '17 at 06:27
  • @cpplearner - To put a rest to it, [there's a working draft from 2005](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf), as close as you could get to C++03. Sub section 5.19 paragraph 3. – StoryTeller - Unslander Monica Jul 03 '17 at 06:32
  • Well my point is given `const float x = 3.f;`, the value of variable `x` cannot be a constant expression (or part of one) in all versions of ISO C++ including C++03. – cpplearner Jul 03 '17 at 06:49
  • @cpplearner - You are wrong (on the first half of your sentence). – StoryTeller - Unslander Monica Jul 03 '17 at 07:01
0

The reason why the int version works is because, well, it cheats.

From [class.static.data]

If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression

For how to actually write correct code, check out StoryTeller's answer

Passer By
  • 19,325
  • 6
  • 49
  • 96