I am looking for help with templates. I need to create function in template what will be reacting differently on a specific type.
It could looks like this:
template <typename T>
class SMTH
{
void add() {...} // this will be used if specific function isn't implemented
void add<int> {...} // and here is specific code for int
};
I also tried to use typeid
and swich
through types in a single function, but doesn't work for me.
You really don't want to be doing this branching at runtime, with typeid
.
We want this code:
int main()
{
SMTH<int>().add();
SMTH<char>().add();
return 0;
}
To output:
int
not int
There are lot of ways I can think of to achieve this (all at compile time and half of them requires C++11):
Specialize the whole class (if it has only this add
function):
template <typename T>
struct SMTH
{
void add() { std::cout << "not int" << std::endl; }
};
template <>
struct SMTH<int>
{
void add() { std::cout << "int" << std::endl; };
};
Specialize only the add
member function (recommended by @Angelus):
template <typename T>
struct SMTH
{
void add() { std::cout << "not int" << std::endl; }
};
template <> // must be an explicit (full) specialization though
void SMTH<int>::add() { std::cout << "int" << std::endl; }
Note that if you instantiate SMTH
with cv-qualified int
, you'll get the not int
output for above approaches.
Use the SFINAE idiom. There are few variants of it (default template argument, default function argument, function return type), and the last one is the one that fits here:
template <typename T>
struct SMTH
{
template <typename U = T>
typename std::enable_if<!std::is_same<U, int>::value>::type // return type
add() { std::cout << "not int" << std::endl; }
template <typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
add() { std::cout << "int" << std::endl; }
};
The main benefit is that you can make the enabling condition complex, e.g. using std::remove_cv
to choose the same overload regardless of cv-qualifiers.
Tag dispatching - chooses the add_impl
overload based on if the instantiated tag inherits from A
or B
, in this case std::false_type
or std::true_type
. You still use template specialization or SFINAE, but this time it's done on a tag class:
template <typename>
struct is_int : std::false_type {};
// template specialization again, you can use SFINAE, too!
template <>
struct is_int<int> : std::true_type {};
template <typename T>
struct SMTH
{
void add() { add_impl(is_int<T>()); }
private:
void add_impl(std::false_type) { std::cout << "not int" << std::endl; }
void add_impl(std::true_type) { std::cout << "int" << std::endl; }
};
This can of course be done without defining the custom tag class, and the code in add
will look like this:
add_impl(std::is_same<T, int>());
I don't know if I mentioned them all, and I don't know why I attempted to, either. All you have to do now is to choose the one that fits the use the best.
Now, that I see, that you also wanted to check if a function exists. This is already long, and there's an existing QA about that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With