Why can't I use float value as a template parameter?

Viewed 64565

When I try to use float as a template parameter, the compiler cries for this code, while int works fine.

Is it because I cannot use float as a template parameter?

#include<iostream>
using namespace std;

template <class T, T defaultValue>
class GenericClass
{
private:
    T value;
public:
    GenericClass()
    {
        value = defaultValue;
    }

    T returnVal()
    {
        return value;
    }
}; 


int main()
{
    GenericClass <int, 10> gcInteger;
    GenericClass < float, 4.6f> gcFlaot;

    cout << "\n sum of integer is "<<gcInteger.returnVal();
    cout << "\n sum of float is "<<gcFlaot.returnVal();

    return 0;       
}

Error:

main.cpp: In function `int main()':
main.cpp:25: error: `float' is not a valid type for a template constant parameter
main.cpp:25: error: invalid type in declaration before ';' token

main.cpp:28: error: request for member `returnVal' in `gcFlaot',
                    which is of non-class type `int'

I am reading "Data Structures for Game Programmers" by Ron Penton, the author passes a float, but when I try it it doesn't seem to compile.

11 Answers

The current C++ standard does not allow float (i.e. real number) or character string literals to be used as template non-type parameters. You can of course use the float and char * types as normal arguments.

Perhaps the author is using a compiler that doesn't follow the current standard?

Just to provide one of the reasons why this is a limitation (in the current standard at least).

When matching template specializations, the compiler matches the template arguments, including non-type arguments.

By their very nature, floating point values are not exact and their implementation is not specified by the C++ standard. As a result, it is difficult to decide when two floating point non type arguments really match:

template <float f> void foo () ;

void bar () {
    foo< (1.0/3.0) > ();
    foo< (7.0/21.0) > ();
}

These expressions do not necessarily produce the same "bit pattern" and so it would not be possible to guarantee that they used the same specialization - without special wording to cover this.

Indeed, you can't use float literals as template parameters. See section 14.1 ("A non-type template-parameter shall have one of the following (optionally cv-qualified) types...") of the standard.

You can use a reference to the float as a template parameter:

template <class T, T const &defaultValue>
class GenericClass

.
.

float const c_four_point_six = 4.6; // at global scope

.
.

GenericClass < float, c_four_point_six> gcFlaot;

Starting with C++20 this is possible.

This also gives the answer to the original question:

Why can't I use float value as a template parameter?

Because nobody implemented it in the standard yet. There is no fundamental reason.

In C++20 non-type template parameters can now be floats and even class objects.

There are some requirements on class objects (they must be a literal type) and fulfil some other requirements to exclude the pathological cases such as user defined operator == (Details).

We can even use auto

template <auto Val>
struct Test {
};

struct A {};
static A aval;
Test<aval>  ta;
Test<A{}>  ta2;
Test<1.234>  tf;
Test<1U>  ti;

Note that GCC 9 (and 10) implements class non-type template parameters, but not for floats yet.

The other answers give good reasons why you probably do not want floating point template parameters, but the real deal breaker IMO is that equality using '==' and bitwise equality are not the same:

  1. -0.0 == 0.0, but 0.0 and -0.0 are not bitwise equal

  2. NAN != NAN

Neither kind of equality is a good cancidate for type equality: Of course, point 2. makes using == invalid for determining type equality. One could use bitwise equality instead, but then x != y does not imply that MyClass<x> and MyClass<y> are different types (by 2.), which would be rather strange.

Related