doetoe
doetoe

Reputation: 781

Partial specializations of templatized alias declarations

In this question I am led to a particular solution which involves partial specializations of templatized alias declarations. The generic case is described in this answer. Suppose I have a template class

template<typename T, ...>
class X {
    // ....
};

Rather than leaving T free and specializing the other template parameters I am in a situation in which the other arguments depend on T, and on T alone. As a very concrete example (more manageable than the example in the other question) consider a template class

template<typename T, T absVal(T)>
class Number_impl {
 private:
  T _t;
 public:
  Number_impl(T t): _t(t) {}
  T abs() const {return absVal(_t);}
};

Possible specializations are

 Number_impl<int, std::abs>;

and

Number_impl<double, std::fabs>;

(I know there are overloaded abs versions, this is just for the sake of illustration. See my other example if you want).

Ideally I would like to define a template class Number depending on a single argument, the type, so that Number<int> is equal to

Number_impl<int, std::abs>;

and Number<double> is equal to

Number_impl<double, std::fabs>;

Something like the following (which doesn't work):

template<typename T>
using Number = Number_impl<T, nullptr>;

template<>
using Number<int> = Number_impl<int, std::abs>;

template<>
using Number<double> = Number_impl<double, std::fabs>;

Does anyone know if and how this can be made to work, or how the same can be achieved in a different way?

Upvotes: 2

Views: 145

Answers (2)

Richard Hodges
Richard Hodges

Reputation: 69922

The normal way to do this kind of thing is the same way the standard library does it - with a traits class that you can specialise:

#include <iostream>
#include <cmath>

template<typename T> struct NumberTraits;

template<typename T, class Traits = NumberTraits<T>>
class Number {
private:
    T _t;
public:
    Number(T t): _t(t) {}
    T abs() const {
        return Traits::abs(_t);
    }
};

template<> struct NumberTraits<int>
{
    static int abs(int i) {
        return std::abs(i);
    }
};

template<> struct NumberTraits<double>
{
    static double abs(double i) {
        return std::fabs(i);
    }
};


using namespace std;

auto main() -> int
{
    Number<int> a(-6);
    Number<double> b(-8.4);
    cout << a.abs() << ", " << b.abs() << endl;
    return 0;
}

expected output:

6, 8.4

Upvotes: 4

Jarod42
Jarod42

Reputation: 218323

You may add a layer:

template<typename T, T absVal(T)>
class Number_impl {
private:
    T _t;
public:
    Number_impl(T t): _t(t) {}
    T abs() const {return absVal(_t);}
};

template<typename T> struct Number_helper;

template<> struct Number_helper<int>    { using type = Number_impl<int, std::abs>; };
template<> struct Number_helper<double> { using type = Number_impl<double, std::fabs>; };

template<typename T>
using Number = typename Number_helper<T>::type;

Upvotes: 3

Related Questions