Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

decltype comparison

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 decltypes.

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.

like image 963
Tom Avatar asked Aug 08 '12 09:08

Tom


3 Answers

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.

like image 120
Xeo Avatar answered Nov 04 '22 07:11

Xeo


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).

like image 6
user541686 Avatar answered Nov 04 '22 06:11

user541686


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.

like image 4
ecatmur Avatar answered Nov 04 '22 06:11

ecatmur