3

I'm trying to create a template function taking a typename. I want to specialize this templates for some basic types like int, long, string and double. For all others types, i need to have a specialized code for class/struct, and a default code for others types.

My current code is this one :

// Declaration
template <typename T, typename enable_if<is_class<T>::value>::type = 0>
void test(T& value);

template <typename T, typename enable_if<!is_class<T>::value>::type = 0>
void test(T& value);

template <> // What am i supposed to write here ?
void test<int>(int& value);

// Definition
template <typename T, typename enable_if<is_class<T>::value>::type = 0>
void test(T& value) {
    cout << "Class/struct test" << endl;
}

template <typename T, typename enable_if<!is_class<T>::value>::type = 0>
void test(T& value) {
    cout << "Other types test" << endl;
}

template <>
void test<int>(int& value) {
    cout << "int test" << endl;
}

This code won't compile. I can't figure what am i suppoed to write in the int specialized template.

I'm trying to use the examples from this documentation, but i'm unable to make it work.

Magus
  • 14,796
  • 3
  • 36
  • 51
  • when `is_class::value` is true, `enable_if::value>::type` is `void`. You try to assign `0` to `void`. – YSC Apr 12 '19 at 08:50
  • Did you read the notes in the link you posted? It almost literally describes your mistake. – D Drmmr Apr 12 '19 at 08:52
  • 1
    It's better to use an overloaded function for this purpose, because you cannot partially specialize a function template. – D Drmmr Apr 12 '19 at 08:55

2 Answers2

5

typename enable_if<is_class<T>::value>::type = 0 doesn't make sense, because typename enable_if<is_class<T>::value>::type would refer to void; you can change it to typename enable_if<is_class<T>::value>::type* = nullptr. Then for the full specialization for int, note that test has two template parameters, then

// Declaration
template <typename T, typename enable_if<is_class<T>::value>::type* = nullptr>
void test(T& value);

template <typename T, typename enable_if<!is_class<T>::value>::type* = nullptr>
void test(T& value);

template <>
void test<int, nullptr>(int& value);

// Definition
template <typename T, typename enable_if<is_class<T>::value>::type*>
void test(T& value) {
    cout << "Class/struct test" << endl;
}

template <typename T, typename enable_if<!is_class<T>::value>::type*>
void test(T& value) {
    cout << "Other types test" << endl;
}

template <>
void test<int, nullptr>(int& value) {
    cout << "int test" << endl;
}

LIVE

Or simply put typename enable_if<is_class<T>::value>::type as the return type. e.g.

// Declaration
template <typename T>
typename enable_if<is_class<T>::value>::type test(T& value);

template <typename T>
typename enable_if<!is_class<T>::value>::type test(T& value);

template <>
void test<int>(int& value);

// Definition
template <typename T>
typename enable_if<is_class<T>::value>::type test(T& value) {
    cout << "Class/struct test" << endl;
}

template <typename T>
typename enable_if<!is_class<T>::value>::type test(T& value) {
    cout << "Other types test" << endl;
}

template <>
void test<int>(int& value) {
    cout << "int test" << endl;
}

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 1
    interestingly it doesn't compile for Visual Studio `Error C2912 explicit specialization 'void test(int &)' is not a specialization of a function template` – Stack Danny Apr 12 '19 at 09:05
  • @StackDanny Emm... It seems the 2nd version works for MSVS. https://rextester.com/QTLR35350 – songyuanyao Apr 12 '19 at 09:10
  • @songyuanyao yes, I just tried it when you edited. works fine. But it's still weird, also has been discussed [here](https://stackoverflow.com/questions/6083948/explicit-specialization-of-c-struct-member-template-functions-is-this-a-visu) – Stack Danny Apr 12 '19 at 09:11
  • `void test(int &)` version also compiles with MSVS. – vahancho Apr 12 '19 at 09:11
0

You need to use std::enable_if in function argument instead of a template parameter:

// Declaration
template <typename T>
void test(T& value, enable_if_t<is_class<T>::value>* = nullptr);

template <typename T>
void test(T& value, enable_if_t<!is_class<T>::value>* = nullptr);

template <>
void test<int>(int& value, enable_if_t<!is_class<int>::value>*);

// Definition
template <typename T>
void test(T& value, enable_if_t<is_class<T>::value>*){
    cout << "Class/struct test" << endl;
}

template <typename T>
void test(T& value, enable_if_t<!is_class<T>::value>*) {
    cout << "Other types test" << endl;
}

template <>
void test<int>(int& value, enable_if_t<!is_class<int>::value>*) {
    cout << "int test" << endl;
}
Dmitry Gordon
  • 2,229
  • 12
  • 20