57

Is it possible to build only some part of the code given the type of the template in C++ ? It would be something lake that :

#include <iostream> using namespace std; template<typename T> void printType(T param) { #if T == char* cout << "char*" << endl; #elif T == int cout << "int" << endl; #else cout << "???" << endl; #endif } int main() { printType("Hello world!"); printType(1); return 0; } 

5 Answers 5

60

Type traits:

#include <iostream> #include <type_traits> // C++0x //#include <tr1/type_traits> // C++03, use std::tr1 template<typename T> void printType(T param) { if(std::is_same<T,char*>::value) std::cout << "char*" << endl; else if(std::is_same<T,int>::value) std::cout << "int" << endl; else std::cout << "???" << endl; } 

Or even better yet, just overload the function:

template<class T> void printType(T partam){ std::cout << "???" << endl; } void printType(char* partam){ std::cout << "char*" << endl; } void printType(int partam){ std::cout << "int" << endl; } 

Partial ordering will take care that the correct function is called. Also, overloading is preferred to template specialization in the general case, see this and this artice for why. Might not apply for you if you totally have to print the type, as implicit conversions are considered for overloaded functions.

Sign up to request clarification or add additional context in comments.

4 Comments

+1 for presenting both alternative (and referring to Sutter's take on template specialization). I will note that the second approach is "better" because more extensible. Notably, for custom types, ADL requires the second approach.
@Matthieu: Yes, but as I said, it might not apply if one is strictly interested in the type, as implicit conversions could be involved. Or is it ambigious if you have both implicit conversion and a fallback template available?
if an implicit conversion is required, then I think the template function would be a better match. I have some difficulties with the rules for function selection and the conversion sequences :)
Note that in the first example std::is_same<T,char*>::value won't handle string literals like "Hello world!" since they would decay into const char* instead of char*, and in the second example void printType(char* partam) won't accept string literals either. So you should consider using std::remove_cv<T>::type in the first example, and const char* in the second example
28

Since C++17 there is a way to do exactly this with if-constexpr. The following compiles since clang-3.9.1, gcc-7.1.0, and recent MSVC compiler 19.11.25506 handles well too with an option /std:c++17.

#include <iostream> #include <type_traits> template<typename T> void printType(T) { if constexpr (std::is_same_v<T, const char*>) std::cout << "const char*" << std::endl; else if constexpr (std::is_same_v<T, int>) std::cout << "int" << std::endl; else std::cout << "???" << std::endl; } int main() { printType("Hello world!"); printType(1); printType(1.1); return 0; } 

Output:

const char* int ??? 

2 Comments

Thanks! fixed and tested. This is actually more powerful, see the related question: stackoverflow.com/q/28075962/1906174. In that situation code won't compile without constexpr
Just be aware that there is a difference between T=char* and T=const char*. You might need to utilize std::remove_cv_t to handle both
11

Use template specialization:

template<typename T> void printType(T param) { // code for the general case - or omit the definition to allow only the specialized types } template<> void printType<char*>(char* param) { // code for char* } template<> void printType<int>(int param) { // code for int } // ... 

1 Comment

Herb Sutter recommend not to use template specialization (on functions) and to simply use regular overload. See gotw.ca/publications/mill17.htm
6

You can use a specialization. The preprocessor runs before all templates and cannot interact with them.

template<typename T> void printType(T t) { std::cout << typeid(T).name(); // fallback } template<> void printType<char*>(char* ptr) { std::cout << "char*"; } template<> void printType<int>(int val) { std::cout << "int"; } 

4 Comments

Herb Sutter recommend not to use template specialization (on functions) and to simply use regular overload. See gotw.ca/publications/mill17.htm
Thanks for the explanation with the preprocessor.
WoW! Let's be cool :) Overloading definitely solves the problem, as demonstrated by Xeo. The issue with specialization is that if you were (suddenly) to introduce a template <typename T> void printType(T* t); overload after the char* specialization, then char *c; printType(c); would call this new overload rather than the "intuitively expected" specialization: ideone.com/F1xS7
@MatthieuM. they are both viable answers, and there are downsides to overloading which only template specialization can address. you can use combinations of both for different use cases and robustness. Saltiness aside, downplaying a unique answer by repeating someone else's recommendation is anti-helpful, plus contributing nothing new.
4

You use template specification to specify versions of your function to work differently based on its type. For example, you can make a generic version of a function that would work with most types, and make a specific version for e.g. int that will be faster. You'd do it this way:

template <class T> void printType(T param) { cout<<"Generic version"<<endl; } template <> void printType<int>(int param) { cout<<"Int version"<<endl; } template <> void printType<char>(char param) { cout<<"Char version"<<endl; } //Rince and repeat. 

1 Comment

Herb Sutter recommend not to use template specialization (on functions) and to simply use regular overload. See gotw.ca/publications/mill17.htm

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.