2

My template specialization does not work. Does anyone know how I can implement this function properly with templates?

template<class T>
float hz_to_nsec(const T &freq) {
    return freq != 0 ? static_cast<float>(NSEC_PER_SEC) / freq : 0;
}

template <>
double hz_to_nsec<double>(const double &freq) {
    return freq != 0 ? static_cast<double>(NSEC_PER_SEC) / freq : 0;
}
dgrat
  • 2,214
  • 4
  • 24
  • 46
  • 2
    You could overload instead of specialising. (See eg http://stackoverflow.com/questions/7108033/template-specialization-vs-function-overloading) – Alan Stokes Jan 06 '16 at 22:56

4 Answers4

7

return type at specialization and primary template must match:

template<class T>
float hz_to_nsec(const T &freq) {
    return freq != 0 ? static_cast<float>(NSEC_PER_SEC) / freq : 0;
}

template <>
float hz_to_nsec<double>(const double &freq) {
^^^^^
    return freq != 0 ? static_cast<float>(NSEC_PER_SEC) / freq : 0;
                                   ^^^^^
}

Alternately, you could provide an overload instead of a template specialization:

template<class T>
float hz_to_nsec(const T &freq) {
    return freq != 0 ? static_cast<float>(NSEC_PER_SEC) / freq : 0;
}

double hz_to_nsec(const double &freq) {
    return freq != 0 ? static_cast<double>(NSEC_PER_SEC) / freq : 0;
}
101010
  • 41,839
  • 11
  • 94
  • 168
  • While this might get rid of the compiler error, the specialization is pretty useless now. – 5gon12eder Jan 06 '16 at 22:55
  • @5gon12eder why is that? – 101010 Jan 06 '16 at 22:57
  • @5gon12eder If you want the specialization to have a different return type, you can write a trait for the return type and specialize the trait. – Brian Bi Jan 06 '16 at 22:58
  • Would you get any different behavior if you'd simply delete the specialization? (Talking about your first example.) I think that the idea was to avoid narrowing `double`s to `float`. – 5gon12eder Jan 06 '16 at 23:13
5

The answer by @101010 addresses your template error. However, you don't need the second function to be a template specialization. It can simply be an overload.

// template <>
// No need to use template specialization.
// Just use an overload.
double hz_to_nsec(double freq) {
    return freq != 0 ? static_cast<double>(NSEC_PER_SEC) / freq : 0;
}
Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

The return type must match. However, it can be templated as well:

template<class T>
T hz_to_nsec(const T &freq) {
    return freq != 0 ? static_cast<float>(NSEC_PER_SEC) / freq : 0;
}

template <>
double hz_to_nsec<double>(const double &freq) {
    return freq != 0 ? static_cast<double>(NSEC_PER_SEC) / freq : 0;
}

Or you can use a trait:

template<class T>
struct RetT
{
    using Type = float;
};

template <>
struct RetT<double>
{
    using Type = double;
};

template<class T>
typename RetT<T>::Type hz_to_nsec(const T &freq) {
    return freq != 0 ? static_cast<float>(NSEC_PER_SEC) / freq : 0;
}

template <>
double hz_to_nsec<double>(const double &freq) {
    return freq != 0 ? static_cast<double>(NSEC_PER_SEC) / freq : 0;
}

Or you don't even need template specialization, you can just overload the function:

template<class T>
float hz_to_nsec(const T &freq) {
    return freq != 0 ? static_cast<float>(NSEC_PER_SEC) / freq : 0;
}

double hz_to_nsec(const double &freq) {
    return freq != 0 ? static_cast<double>(NSEC_PER_SEC) / freq : 0;
}
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
StenSoft
  • 9,369
  • 25
  • 30
1

I believe that specialization is the wrong tool here. Instead, consider making the target type an additional type parameter.

// C++14

template <typename OutputT, typename InputT>
std::enable_if_t
<
  std::is_arithmetic<InputT>::value && std::is_floating_point<OutputT>::value,
  OutputT
>
hz_to_nsec(const InputT freq)
{
  return (freq != InputT {0})
    ? static_cast<OutputT>(NSEC_PER_SEC) / static_cast<OutputT>(freq)
    : OutputT {0};
}

I'm putting OutputT first because it cannot be deduced. I have also restricted the permissible types for InputT and OutputT to what might be sensible types.

It can be used like this.

hz_to_nsec<double>(10);    // InputT = int,           OutputT = double
hz_to_nsec<float>(10.0f):  // InputT = float,         OutputT = float
hz_to_nsec<float>(5UL);    // InputT = unsigned long, OutputT = float
5gon12eder
  • 24,280
  • 5
  • 45
  • 92