Search code examples
c++templatesc++11decltypetemplate-argument-deduction

decltype succeeds where template argument deduction fails?


I'd like to know why the following code compiles and works as expected when the line with bbb isn't commented out but the line before it is, but it does fail when the line marked with aaa isn't commented out and line bbb is:

#include <iostream>
#include <string>
using String = std::string;
struct Person {
    String m_name;
    explicit Person(String const &name): m_name(name) {}
    operator String() const { return "\"" + m_name + "\""; }
};

template<class T> bool isEqual(
T const& a,
//T const& // does NOT compile // aaa
decltype(a) // DOES compile    // bbb
b){ return a == b; }

int main()
{
    String const  plain("plain");
    Person const jb("James");
    bool b = isEqual(plain, jb);
    std::cout << "isEqual(plain, person) is " << ( b ? "true" : "false" ) << "\n";
}

Solution

  • When you do:

    template<class T> 
    bool isEqual(T const& a, T const& b)
    

    the Ts deduce as different types (String for a and Person for b). Since T can only be one type, that's why deduction fails.

    However, when you do:

    template<class T> 
    bool isEqual(T const& a, decltype(a) b)
    

    only one parameter/argument pair needs to be deduced. T gets deduced as String, and since Person can be convertible to String (via the conversion function operator String()), this works just fine.

    This is similar to the identity trick where we simply force one parameter to be in a non-deduced context:

    template <typename T>
    struct identity { using type = T; };
    
    template <typename T>
    bool isEqual(T const& a, typename identity<T>::type const& b);
    

    Here again, there is only one pair for which type deduction happens, T is deduced as String - and so the type of b is also String const&, but not because it was deduced as such - because it was substituted in.