Reputation: 1
I want to write a template function which calls a different method based on type of template argument. Ex-
template<typename T>
void GetData(T& data)
{
if(T is int)
Call_Int_Method(data);
else if(T is double)
Call_Double_Method(data); // This call will not compile if data is int
...
}
Basically, GetData should only be calling one method based on type of T. Other calls should not exist. Is it possible somehow?
Upvotes: 0
Views: 89
Reputation: 39109
Overloads might be the preferred solution here, let alone because they put a lower maintenance burden on you:
void GetData(int& data)
{
Call_Int_Method(data);
}
void GetData(double& data)
{
Call_Double_Method(data); // This call will not compile if data is int
}
Or, depending on the innards of Call_(int|double)_Method
, maybe you can overload them:
template <typename T>
void GetData (T &data) {
CallMethod (data);
}
void CallMethod (int &) {}
void CallMethod (double &) {}
But again, possibly is is better maintainable if you restrict the type of T
, using overloading again:
void GetData (int &data) { CallMethod (data); }
void GetData (double &data) { CallMethod (data); }
You can also restrict the instantiable types in templates with type traits, but that might be less maintainable.
But maybe your situation is different, and you have a template that works for all types, except some cherry-picked ones:
// general template
template <typename T>
void GetData (T &data) {...}
// function, technically not an overload
void GetData (std::string &data) {....}
// this is an overload of the GetData(std::string&)-function:
void GetData (std::map<int,int> &) {....}
There is plenty to choose from and no single correct answer. Possibly giving you some rules of thumbs for maintainable code is best:
A good readability check is to let your code rest for some days and review it again to see if you still know what you have done. If everything's okay, repeat this test at larger time intervals.
Upvotes: 1
Reputation: 7881
Since no one is putting this here yet, I thought I'd give another option (that may be overkill, simple specialization/overloading is much more readable).
You can use SFINAE (substution failure is not an error) in C++11 (or with boost (or with your own special classes)) if what you are looking to do is select types that meet a certain criteria. While your example is if T
is an int
or T
is a double
, what if I want to make it more general than that? I want to select if T
is ANY integral type, and if T
is ANY floating point type?
#include <string>
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value,T>::type foo(T val)
{
std::cout << "floating function" << std::endl;
return val;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value,T>::type foo(T val)
{
std::cout << "integral function" << std::endl;
return val;
}
int main(int argc,char** argv)
{
foo(2.0);//T==double, floating point function
foo(2);///T==int, integral function
foo(2.0f);//T==float, floating point function
//foo<std::string>(); throws no matching function for call to foo()
}
What is happening here? well std::enable_if is a template who ONLY has a member type
iff val==true
. This makes it so that std::enable_if<false,T>::type
is an invalid statement. The compiler doesn't automatically throw this as an error. Instead, it looks for other things that it can do.
std::is_floating_point
and std::is_integral
simply return the trait values of that type.
Honestly, I like this a little better than simple specialization because it is more easily expandable: you've now allready written your function for unit8_t
, long double
, float
, ect. You can still do specialization on top of this, if any of those need to be different. And compared to overloading, you can more easily explicitly select which function you're talking about (if you want to get the function pointer, &foo<int>
is tons easier and more readable than (int(*)(int))&foo
). Like all template methods, it is a compiler time operation, so it shouldn't negatively effect your run-time at all.
The downside? Well, the function declaration is now 99 characters long...
Upvotes: 1
Reputation: 227578
Provide some overloads, either of GetData
if you need the whole function to have specific behaviour for int
or double
:
void GetData(int& data)
{
Call_Int_Method(data);
}
void GetData(double& data)
{
Call_Double_Method(data);
}
or the Call_X_Methods
if only a portion of the function's logic is to be specialized:
template<typename T>
void DoTypeSpecificStuff(T& data) { /* stuff */ }
void DoTypeSpecificStuff(int& data) { .... }
void DoTypeSpecificStuff(double& data) { .... }
template<typename T>
void GetData(T& data)
{
DoTypeSpecificStuff(data);
// do other stuff
....
}
Note that overload resolution will favour non-templates over function templates when the types match, so you can be sure the right function will be called for these two types.
Upvotes: 8