Search code examples
c++templatesc++11decltype

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.


Solution

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