say you have a class like this:
template <typename T>
class MyClass {
public:
void doSomething();
};
And within a method, is there any way to have different behaviour depending on the type like that:
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
if(T == int) {
int myInt = atoi(value.c_str());
} else if(T == std::string) {
std::string myString = value;
}
}
Is there any easy way to achieve this?
I know I could write a wrapper class that provides a constructor that takes a std::string
, so I could use something like:
T myVar(value);
But that would be more resource wasting if using simple type like an int
, also I would like to realize that if-else-statement to be able to perform different actions.
I would be thankful for an answer. regards,
tagelicht
In C++17, you can achieve exactly what you want with if constexpr(...)
:
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
if constexpr(std::is_same_v<T, int>) {
int myInt = atoi(value.c_str());
} else constexpr(std::is_same_v<T, std::string>) {
std::string myString = value;
}
}
In C++14, you can implement your own static_if
quite easily. I gave a tutorial talk about this at CppCon 2016 and Meeting C++ 2016.
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
static_if(std::is_same_v<T, int>)
.then([&](auto) {
int myInt = atoi(value.c_str());
})
.else_if(std::is_same_v<T, std::string>)
.then([&](auto) {
std::string myString = value;
})();
}
In C++11, you probably want to use function overloading or template specialization. Example with the former (updated with suggestions from Jarod42 in the comments):
template <typename> struct tag { };
void call_dispatch(...) { } // lowest priority
void call_dispatch(tag<std::string>)
{
std::string myString = value;
}
void call_dispatch(tag<int>)
{
int myInt = atoi(value.c_str());
}
Usage:
template <typename T> MyClass::doSomething() {
//this is what I want:
std::string value = "17";
call_dispatch(tag<T>{});
}
Two approaches not yet exemplified are 1) tag dispatch and 2) template specialization
The way we do this is to define some empty templated struct as a lightweight "tag" type:
template <typename T>
class MyClass {
public:
void doSomething();
private:
// tag for dispatch
template<class U>
struct doSomethingTag{};
... and then define helper functions that take specializations of that tag:
void doSomethingHelper(doSomethingTag<std::string>, std::string value);
void doSomethingHelper(doSomethingTag<int>, std::string value);
};
Then we can use the primary entry point (doSomething
) as a way to call the specialized helper function:
template <typename T>
void MyClass<T>::doSomething() {
//dispatch to helper function
doSomethingHelper(doSomethingTag<T>{}, "17");
}
The helper functions look like this:
template <typename T>
void MyClass<T>::doSomethingHelper(doSomethingTag<std::string>, std::string value)
{
int myInt = atoi(value.c_str());
}
template <typename T>
void MyClass<T>::doSomethingHelper(doSomethingTag<int>, std::string value)
{
std::string myString = value;
}
This should incur no overhead for the tag structures because the helper functions don't actually use the tag parameter, so it'll be optimized away. If it's not, it is a 1-byte overhead to construct the struct. (Note: Vittorio Romeo hinted at this in his answer, but he called it "function overloading")
You can "specialize" the template for your function so long as you are fully specializing it (specify the type for the class!):
The syntax for specialization looks a little strange because we keep the angle-brackets empty: <>
:
template<>
void MyClass<int>::doSomething() {
//dispatch to helper function
std::string value = "17";
int myInt = atoi(value.c_str());
}
template <>
void MyClass<std::string>::doSomething()
{
std::string value = "17";
std::string myString = value;
}
This enables you to keep a smaller class:
template <typename T>
class MyClass {
public:
void doSomething();
};
In the specific example you've given, I'd prefer template specialization. However, oftentimes you want to do some kind of partial specialization, and this gets very very strange with member functions, so tag dispatch is often preferred.
With C++17, using constexpr if definitely makes life easier.
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