c++templatesdecltypedeclval

How does `decltype(true ? std::declval<T1>() : std::declval<T2>())` works?


I saw the example code for template function max.

template <typename T1,
          typename T2,
          typename RT = std::decay_t<decltype(true ? std::declval<T1>()
                                                   : std::declval<T2>())>>
RT max (T1 a, T2 b) {
    return b < a ? a : b;
}

I know what is decltype and declval. But I couldn't understand why it uses ternary operator with always true to deduce return type.

If I use max(1, 2.5) it returns double type of 2.5, which means RT is deduced to second parameter T2 or double. How is it possible even though decltype returns only type of T1?

(I know I can use auto for return type to simplify code. But my purpose is to understand the usage of weird code.)


Solution

  • std::decay_t<decltype(true ? std::declval<T1>() : std::declval<T2>())> is a way of obtaining the "common type" of T1 and T2. Presumably, if T1 = int and T2 = double, this is intended to be double because int would be converted to double in an expression such as int{...} + double{...}.

    See What is the result type of '?:' (ternary/conditional operator)?

    Note that std::declval returns an rvalue reference, and std::decay_t removes that reference qualification so that RT is not a reference, but an object type.

    More commonly, people use std:common_type_t which uses the conditional operator as one of the ways of obtaining a common type.

    template <typename T, typename U, typename R = std::common_type_t<T, U>>
    R max(T a, U b) {
        return b < a ? a : b;
    }
    

    Or alternatively:

    // C++20 abbreviated function template
    // C++11 trailing return type with decltype
    auto max(auto a, auto b) -> decltype(b < a ? a : b) {
        return b < a ? a : b;
    }