I have a C++ function that looks like this:
template <class T> void MyClass::set(T value) {
if (std::is_same<T, std::string>::value) {
stringValue_ = value;
} else if (std::is_same<T, int>::value) {
intValue_ = value;
}
}
However, I'm getting compiler errors. Apparently it believes that the type of T is always a std::string:
assigning to 'int' from incompatible type 'std::__cxx11::basic_string<char>'
Additionally, when I try to cast the value to an int like (int)value, I get casting errors. The entire point of this function is to take in a template parameter value and then assign it to the correct variable of the class based on the parameter's actual type. Please let me know which misconceptions or errors I'm making here.
Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.
Explanation: A template parameter is a special kind of parameter that can be used to pass a type as argument.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
Template parameters may have default arguments. The set of default template arguments accumulates over all declarations of a given template.
You cannot cast a template parameter, in the manner you're trying to do:
template <class T> void MyClass::set(T value) {
if (std::is_same<T, std::string>::value) {
stringValue_ = value;
} else if (std::is_same<T, int>::value) {
intValue_ = value;
}
}
The key concept here is that a template function gets expanded in its entirety when it gets instantiated.
If say, for a given template instance, the T
template parameter is an integer. This template then get instantiated in approximately the following manner:
void MyClass::set(int value)
{
if (false)
{
stringValue_=value;
}
Your attempt here to set stringValue
, that's presumably a std::string
, to an int
, is not going to be very successful. Just because the if
condition is false, does not make this chunk of code go away. It still must be valid C++, even if it never gets executed, and this is not valid C++. This is the explanation for your compilation error.
Some features in the newest C++17 standard will make these kinds of constructs actually possible. However, pre-C++17 the general approach to solving this kind of a problem is to use template specialization:
template<typename T> void MyClass::set(T value);
template<> void MyClass::set<int>(int value)
{
intValue_=value;
}
template<> void MyClass::set<std::string>(std::string value)
{
stringValue_=value;
}
Or, forget templates entirely, and just define two separate set()
methods.
The reason for the compiler error is that both branches of the if
statement will be instantiated with the template. This is obviously an error because if T = int
there is not assignment operator to std::string
. C++17 introduces a method to disregard one of the branches at compile-time, called if constexpr
:
template <class T> void MyClass::set(T value) {
if constexpr (std::is_same<T, std::string>::value) {
stringValue_ = value;
} else if (std::is_same<T, int>::value) {
intValue_ = value;
}
}
But actually, don't use a template here (and don't use SFINAE here either!). Just use good old function overloading.
class MyClass
{
std::string stringValue_;
int intValue_;
public:
void set(int);
void set(std::string);
};
void MyClass::set(int value) {
intValue_ = value;
}
void MyClass::set(std::string value) {
stringValue_ = value;
}
One could argue that SFINAE is actually necessary if you are trying to cover a whole range of types which are convertible into each other.
class MyClass
{
std::string stringValue_;
int intValue_;
public:
template < typename T, typename std::enable_if< std::is_arithmetic<T>::value, void** >::type = nullptr >
void set(T value) {
intValue_ = value;
}
template < typename T, typename std::enable_if< std::is_same<T,std::string>::value, void** >::type = nullptr >
void set(T value) {
stringValue_ = value;
}
};
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