Is there a way to compare the result of decltype
in C++11?
In other words, why is this code invalid:
template<typename T, typename U>
void func(T& t, U& u) {
if(decltype(t) == decltype(u)) {
// Some optimised version for this case
} else {
// A more general case for differing types
}
}
I know that in some cases this particular problem can be solved by partial template specialisation; my question is about comparison of decltype
s.
Edit: The question came up in the course of trying to provide defaults for free functions through SFINAE. Perhaps a better question would have been why this is invalid:
template<bool B>
bool SomeFunction() { ... }
template<typename T, typename U>
bool SomeFunctionWrapper(T& t, U& u) {
SomeFunction<decltype(t) == decltype(u)>();
}
I've since found another solution (that doesn't involve templates at all) but at one stage I tried this:
// If it exists, the free function is defined as
// bool AFreeFunction();
typedef struct { char } undefined;
template<typename T = void>
undefined AFreeFunction();
template<bool B>
bool AFreeFunctionWrapper_() {
return false;
}
template<>
bool AFreeFunctionWrapper_<false>() {
return AFreeFunction();
}
bool AFreeFunctionWrapper() {
return AFreeFunctionWrapper_<decltype(AFreeFunction()) == decltype(undefined)>();
}
I eventually got a variant of this strategy working with GCC 4.6, but then discovered that default template arguments are not allowed for template functions in MSVC, even in the 2012 RC. So the eventual solution looks like this:
class AFreeFunction {
public:
operator bool() { return false; }
};
If the function is defined, it gets called. If it's not, it is instead interpreted as a constructor for the class, which is then implicitly cast to bool
.
You normally solve this problem through tag dispatching. Also, you already have the "declared type" of t
and u
- T&
and U&
, respectively. To compare types for equality, you can use std::is_same
. However, overload resolution already solves this problem for you:
template<class T>
void f(T& v1, T& v2){ ... } // #1
template<class T, class U>
void f(T& t, U& u){ ... } // #2
#1 is more specialized than #2 if both arguments to f
are of the same type. If you, for whatever reason, insist on solving this through manual type comparision, here's how it would look applying the before mentioned points:
#include <type_traits>
namespace detail{
template<class T, class U>
void f(T& t, U& u, std::true_type){ ... } // #1
template<class T, class U>
void f(T& t, U& u, std::false_type){ ... } // #2
} // detail::
template<class T, class U>
void f(T& t, U& u){
detail::f(t, u, std::is_same<T,U>()); // tag dispatching
}
std::is_same
will derive from std::true_type
if both types are the same, and from std::false_type
if not.
Why is it invalid? The result of a decltype
is, well, a type. So it's saying something like
if (int == int)
which the language obviously doesn't allow.
You'll need to separate the two parts of the function and put the specialized part inside a function in a specialized class, and forward the call there. It's painful.
Or, you could use typeid
or run-time type information, if your implementation implements it correctly, although that will defer everything to when the program runs (which allows for fewer optimizations).
You can do this using SFINAE (std::enable_if
):
template<typename T, typename U>
typename std::enable_if<std::is_same<T, U>::value, void>::type func(T& t, U& u) {
std::cout << "same\n";
}
template<typename T, typename U>
typename std::enable_if<!std::is_same<T, U>::value, void>::type func(T& t, U& u) {
std::cout << "different\n";
}
As Mehrdad says, decltype(t)
and decltype(u)
are types (T &
and U &
respectively), not values, so cannot be compared at value-expression level but must be compared at meta-expression (template) level.
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