6

I have a class which allows for a vector to be created holding any type or class. However I'd like to add additional functionality for numerical types.

template <>
class Vec<double> : public VecBase<double>
{
    //  == METHODS ==
    public:
        //  -- Constructors & Destructors --
        explicit Vec(const unsigned long long t_size);
        virtual ~Vec();

        //  -- Operators --
        friend Vec<double> operator+(const Vec<double>&, const double);

        //  -- Methods --
        double sum();

... etc.

I have partially specialised the class template to allow overloading of mathematical operators for double specialisation. I'd now like to extend this specialisation to int as well, but rather than copy the specialisation replacing double with int, is there a way to add it into the specialisation list?

That is, is there any way to allow for:

template<>
class Vec<double (or) int>

Cheers!

user7119460
  • 1,451
  • 10
  • 20

3 Answers3

3

I suppose you can use a boolean default value, like in foo struct in the following example

#include <iostream>

template <typename>
struct isSpecialType
 { static constexpr bool value { false }; };

template <>
struct isSpecialType<int>
 { static constexpr bool value { true }; };

template <>
struct isSpecialType<double>
 { static constexpr bool value { true }; };

template <typename T, bool = isSpecialType<T>::value>
struct foo;

template <typename T>
struct foo<T, true>
 { static constexpr bool value { true }; };

template <typename T>
struct foo<T, false>
 { static constexpr bool value { false }; };

int main()
 {
   std::cout << "- void value:   " << foo<void>::value << std::endl;
   std::cout << "- bool value:   " << foo<bool>::value << std::endl;
   std::cout << "- int value:    " << foo<int>::value << std::endl;
   std::cout << "- double value: " << foo<double>::value << std::endl;
 }

The idea is define a sort of type traits (isSpecialType) to choose the selected types (int and double, in your example) with a booleand value that is false in the generic implementation and true in the specializations.

template <typename>
struct isSpecialType
 { static constexpr bool value { false }; };

template <>
struct isSpecialType<int>
 { static constexpr bool value { true }; };

template <>
struct isSpecialType<double>
 { static constexpr bool value { true }; };

Next you have to declare the foo struct (class Vec, in your question) with a supplemental bool template value with the isSpecialType<T>::value default value

template <typename T, bool = isSpecialType<T>::value>
struct foo;

Last, you have to implement two partially specialized version of foo: the first one with the boolean true value

template <typename T>
struct foo<T, true>
 { static constexpr bool value { true }; };

corresponding to the specialized version of your Vec; the one with the false boolean value

template <typename T>
struct foo<T, false>
 { static constexpr bool value { false }; }; 

corresponding to the generic version of your Vec.

Another point: my example is C++11 or newer code; if you want a C++98 version, you have only to define the bool values as const (instead constexpr) and initialize they whit the C++98 style; I mean

 static bool const bool value = true;

instead of

 static constexpr bool value { true };
max66
  • 65,235
  • 10
  • 71
  • 111
2

There sure is but you might find this already done in http://en.cppreference.com/w/cpp/numeric/valarray

have a look at std::enable_if and std::is_integral and std::is_floating_point. (copied from cplusplus.com)

// enable_if example: two ways of using enable_if
#include <iostream>
#include <type_traits>

// 1. the return type (bool) is only valid if T is an integral type:
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
  is_odd (T i) {return bool(i%2);}

// 2. the second template argument is only valid if T is an integral type:
template < class T,
           class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}

int main() {

  short int i = 1;    // code does not compile if type of i is not integral

  std::cout << std::boolalpha;
  std::cout << "i is odd: " << is_odd(i) << std::endl;
  std::cout << "i is even: " << is_even(i) << std::endl;

  return 0;
}
Captain Giraffe
  • 14,407
  • 6
  • 39
  • 67
2

I have same idea with @max66, but you can use a helper function to do this a bit easier.

#include <iostream>
#include <type_traits>

// helper
template <typename ...Ts>
struct allowed_types
{
    template <typename T>
    using check = std::disjunction<std::is_same<T, Ts>...>;

    template <typename T>
    inline static constexpr bool check_v = check<T>::value;
};

// usage
template <typename T, bool = allowed_types<double, float>::check_v<T>>
struct foo;

template <typename T>
struct foo<T, true> // for double and float
{
    inline static constexpr size_t value = 1;
};

template <typename T>
struct foo<T, false> // for other types
{
    inline static constexpr size_t value = 2;
};

int main()
{
    std::cout << foo<float>::value << '\n';  // 1
    std::cout << foo<double>::value << '\n'; // 1
    std::cout << foo<int>::value << '\n';    // 2
    std::cout << foo<char>::value << '\n';   // 2
}

Just put any set of types you need. For example:

template <typename T, bool = allowed_types<char, int, std::vector<int>>::check_v<T>>

EDIT:
If you need to split your specializations more than on 2 groups, then you need to use enable_if approach. With helper from above it could be written like this:

// default, for any type
template <typename T, typename = void>
struct foo
{
    inline static constexpr size_t value = 1;
};

// for double and float
template <typename T>
struct foo<T, std::enable_if_t<allowed_types<double, float>::check_v<T>>>
{
    inline static constexpr size_t value = 2;
};

// for int and char
template <typename T>
struct foo<T, std::enable_if_t<allowed_types<int, char>::check_v<T>>>
{
    inline static constexpr size_t value = 3;
};

int main()
{
    std::cout << foo<bool>::value << '\n';   // 1
    std::cout << foo<double>::value << '\n'; // 2
    std::cout << foo<float>::value << '\n';  // 2
    std::cout << foo<int>::value << '\n';    // 3
    std::cout << foo<char>::value << '\n';   // 3
}
d_kog
  • 143
  • 6