umebe
umebe

Reputation: 111

Function specialization in template class for float and double literals

I'm trying to find a solution to have constant numeric literals inside template class method. I'm making some math template classes to be used with float or double types. The problem is that literals are different depending on data type (for example "0.5f" for float and "0.5" for double). So far, I come up with two solutions. Some hypothetical code for first one:

template <typename T>
class SomeClass
{
    public:
        T doSomething(T x);
};

template <>
float SomeClass<float>::doSomething(float x)
{
    float y = 0.5f;
    /*
     * Do computations...
    */
    return x;
}

template <>
double SomeClass<double>::doSomething(double x)
{
    double y = 0.5;
    /*
     * Do computations...
    */
    return x;
}

The approach above forces rewriting whole methods for every type it is used with.

Another approach:

template <typename T>
class SomeClass
{
    public:
        T doSomething(T x);

    private:
        T getValue();
};

template <typename T>
T SomeClass<T>::doSomething(T x)
{
    T y = getValue();
    /*
     * Do computations...
    */
    return x;
}

template <>
float SomeClass<float>::getValue()
{
    return 0.5f;
}

template <>
double SomeClass<double>::getValue()
{
    return 0.5;
}

This one doesn't require to write same methods multiple times for specific type, but requires to have a lot getValue() methods for every "magic number" that need to be used inside the method.

Is there another, "more elegant" way to solve this?

Upvotes: 10

Views: 6604

Answers (3)

umebe
umebe

Reputation: 111

Thank you all for your answers and comments. I allowed myself to make a summary of what has been said so far and add my conclusions.

I'm going to implement some math class template to be instantiated to use with float or double type. There is a need to use some numeric literals internally in the class. They will be some commonly used numeric literals and constants, such as 0.0, 0.5, 1.0, pi, etc. I'm looking for a solution to make class instantiation work on different literals depending on its type.

Whether or not use float and double literals?

Talk went slightly on the topic whether or not bother to use separate literals for float and double. This might be caused by a bit unfortunate example I gave in my question. In the example literal will be converted to proper type at compile time anyway, so no harm done. But in general there will be cases where literal needs to be used in expression, for example:

float foo(float x)
{
    return x * 3.14;
}

This will force compiler to convert x to double, make computations, then convert result back to float. Pros and cons of such behavior:

Pros:

  • Precision gain because actual computations will be done in double precision.

Cons:

  • If performance is an issue, this might result in faster as well as slower execution, depending on environment and platform. This introduces some performance change depending on implementation, which is as far as I'm considered, bad.
  • Introduces computations inconsistency because some operations will be done on floats and some on doubles, depending on literals use. This might also open some bugs exposure.
  • It breaks the idea to specialize class for floats in the first place because internally computations will be done on doubles anyway.

In conclusion, the goal is to make class instantiation work on proper types without any additional conversions. The idea to use double type literals everywhere, such as 0.5 and leave handling proper conversion to compiler is not an option.

More on the subject: Should we generally use float literals for floats instead of the simpler double literals?

Possible solutions?

  • Specialize methods for every template instantiation type where literals have to be used. This is probably worst solutions because it forces to write the same code twice with minor changes to the literals.
  • Make specialized getValue<type>() methods, or as Jonathan Wakely posted - specialized members. This might lead to have some silly named members or methods in the class, such as getZeroPointFive<float>().
  • Mohammad and Tony Delroy pointed out that wrapping every literal with static_cast<T>() will do the trick. Conversion to proper type will be done at compile time.

Upvotes: 1

Tony Delroy
Tony Delroy

Reputation: 106196

You just don't need to worry about this - use 0.5 and if the type is float the compiler will still initialise it optimally to the same value as if you'd used 0.5f.

It's a bit long-winded, but you might want to read through the "other mechanisms supporting polymorphism" part of my answer here: Polymorphism in c++

For more general usage of the float constant throughout the function - particularly in comparisons and expressions - it's worth reading the link bluemess mentions below....

Upvotes: 0

Jonathan Wakely
Jonathan Wakely

Reputation: 171383

Assuming it's actually necessary to use different values in the two specializations (it's not necessary for 0.5 and 0.5f, for example) it would be a lot less typing to do:

template <typename T>
class SomeClass
{
  public:
    T doSomething(T x);

  private:
    static const T magic_number_1;
};

template <typename T>
T SomeClass<T>::doSomething(T x)
{
  T y = magic_number_1;
  /* 
   * Do computations...
  */
  return x;
}

template <>
const float SomeClass<float>::magic_number_1 = 0.5f;

template <>
const double SomeClass<double>::magic_number_1 = 0.5;

Upvotes: 1

Related Questions