Template specialization with enable_if
Asked Answered
P

2

5

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.

Papen answered 12/4, 2019 at 8:45 Comment(4)
when is_class<T>::value is true, enable_if<is_class<T>::value>::type is void. You try to assign 0 to void.Seventeenth
Did you read the notes in the link you posted? It almost literally describes your mistake.Agra
It's better to use an overloaded function for this purpose, because you cannot partially specialize a function template.Agra
The previous comment is important. I was trying to limit the allowed types to two by declaring a function with enable_if in the header and explicitly instantiating the template function in the source file but I got an undefined symbol because the enable_if becomes part of the symbol in the binary. When you explicitly instantiate the function, you can't add enable_if, so it has to stay undefined. The example here is different because the test function is declared and defined explicitly for int, hence the enable_if is not part of the symbol in the generated code, so the answers helpTombac
C
6

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

Canea answered 12/4, 2019 at 8:59 Comment(4)
interestingly it doesn't compile for Visual Studio Error C2912 explicit specialization 'void test<int,nullptr>(int &)' is not a specialization of a function templateGautea
@StackDanny Emm... It seems the 2nd version works for MSVS. rextester.com/QTLR35350Canea
@Canea yes, I just tried it when you edited. works fine. But it's still weird, also has been discussed hereGautea
void test<int>(int &) version also compiles with MSVS.Unbalance
N
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;
}
Notum answered 12/4, 2019 at 8:56 Comment(2)
It's perfectly fine to put it as a template parameter if done properly. This pollutes the function signature, and IMO is harder to read as well.Tangle
Yeah, agree. The template parameter is betterNotum

© 2022 - 2024 — McMap. All rights reserved.