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

Solution 1:

THE SIMPLE ANSWER

The standard doesn't allow floating points as non-type template-arguments, which can be read about in the following section of the C++11 standard;

14.3.2/1      Template non-type arguments      [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:

  • for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter;

  • the name of a non-type template-parameter; or

  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or

  • a constant expression that evaluates to a null pointer value (4.10); or

  • a constant expression that evaluates to a null member pointer value (4.11); or

  • a pointer to member expressed as described in 5.3.1.


But.. but.. WHY!?

It is probably due to the fact that floating point calculations cannot be represented in an exact manner. If it was allowed it could/would result in erroneous/weird behavior when doing something as this;

func<1/3.f> (); 
func<2/6.f> ();

We meant to call the same function twice but this might not be the case since the floating point representation of the two calculations isn't guaranteed to be exactly the same.


How would I represent floating point values as template arguments?

With C++11 you could write some pretty advanced constant-expressions (constexpr) that would calculate the numerator/denominator of a floating value compile time and then pass these two as separate integer arguments.

Remember to define some sort of threshold so that floating point values close to each other yields the same numerator/denominator, otherwise it's kinda pointless since it will then yield the same result previously mentioned as a reason not to allow floating point values as non-type template arguments.

Solution 2:

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?

Solution 3:

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.

Solution 4:

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;