c++c++11templatesdecltypetrailing-return-type

c++ 11 Trailing return type with decltype does not work as expected


Why this works "as expected"? My understanding is that this should not work:

template <class T, class U>
auto x(T a, U b) -> decltype(a<b ? a:b) {
    return a > b ? a : b;
}


int main() {
    cout << x<long, double>(1, 2.01) << endl;
    cout << x<long, double>(5, 2.01) << endl;

}

I have tried some other combinations such as:

template <class T, class U>
auto x(T a, U b) -> decltype(a<b ? a:a) {
    return a > b ? a : b;
}

This way it does not compile with error Actually the second combination fails with

compile time error:  Error C2440 'return': cannot convert from 'U' to 'T &' 

which is expected. My understanding is that the first function also should fail with the same error, while it works fine.


Solution

  • It doesn't matter what the condition with the ?: operator is. The result type is calculated as a common type of the second and third operand. Here is part of how the common type and the value category of the ?: operator are calculated, see cppreference.com for the full details:

    If the second and third operand are lvalues of the same type, then the result type will be a lvalue of that type.

    If the types are unrelated lvalues, there are some more complex rules to determine the common type, but the result will be a prvalue, not a lvalue. In particular if the two types are arithmetic types such as double and long, then the usual arithmetic conversions are applied to obtain a common type. In the case of long and double that common type would be double. This is the same type calculation that would be done if you e.g. tried to add two different arithmetic type with +, hence the name usual arithmetic conversions.

    Therefore decltype(a<b ? a:b) will be a reference type if a and b have the same type and otherwise it will not be a reference type.

    This is why the function compiles. The common type is always such that both input types can be converted to it. It is also why the function has undefined behavior if the types are equal, because then decltype gives a reference and so you are going to return a reference to one of the function parameters.

    decltype(a<b ? a:a) doesn't work with different types, because the common type of a and a is, as described above, a reference of the type of a. If b then has a different unrelated type, the result of a > b ? a : b will be a prvalue which cannot be bound to a lvalue reference.