This is from: https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/std/type_traits
template<typename _Xp, typename _Yp>
using __cond_res
= decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());
...
template<typename _Tp1, typename _Tp2>
struct __common_reference_impl<_Tp1, _Tp2, 3,
void_t<__cond_res<_Tp1, _Tp2>>>
{ using type = __cond_res<_Tp1, _Tp2>; };
I'm trying to figure out what _Xp(&)()
is - is it a function call signature? i.e. a constructor? Doesn't really make sense. It seems there is a anonymous variable name there, i.e.:
_Xp(&anon)()
I still can't wrap my head around it somehow and I've been coding C++ for the last 34 years.
Any explanation is appreciated. Thanks.
tl;dr; We need a way to produce an expression with type and value category T
, not type and value category T&&
, so we can't just use std::declval<T>()
and instead need to do something else.
The point of this:
template<typename _Xp, typename _Yp>
using __cond_res
= decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());
is to give you the type of false ? x : y
where x
is an expression of type and value category _Xp
and y
is an expression of type and value category _Yp
.
The conditional operator (usually called the ternary operator), ?:
, is an extremely complicated language feature. It's one of the places in the language where there is actually a differentiation between prvalues and xvalues.
The naive way to implement this would be:
template<typename _Xp, typename _Yp>
using __cond_res
= decltype(false ? declval<_Xp>() : declval<_Yp>());
Because, well, isn't that what declval<T>()
is for, to give you a T
? But actually, there's a flaw here, because declval
isn't specified as:
template <typename T>
auto declval() -> T;
It's specified as (add_rvalue_reference_t<T>
rather than T&&
to correctly handle void
):
template <typename T>
auto declval() -> std::add_rvalue_reference_t<T>;
As a result, __cond_res<int, int>
and __cond_res<int&&, int&&>
would be indistinguishable, even though the first needs to be int
while the latter needs to be int&&
.
So, we need a way to actually produce an arbitrary expression of type T
. One way would to just actually:
template <typename T>
auto better_declval() -> T;
template<typename _Xp, typename _Yp>
using __cond_res
= decltype(false ? better_declval<_Xp>() : better_declval<_Yp>());
This works.
An alternative is to produce an instance of a function that gives you T
and then invoke it. That's what declval<_Xp(&)()>()()
does - gives you a reference to a nullary function that returns a _Xp
, and then invokes it, giving you an _Xp
(of the correct value category).
In this case, this seems like unnecessary complexity compared to the better_declval
approach, but it turns out that this pattern is useful in other contexts as well. Like concepts:
template <typename T>
concept something = requires (T(&obj)()){
f(obj());
};
Here, I have a concept that checks to see if I can call f
with an expression of type T
, including differentiating between prvalues and xvalues correctly. The above is the most convenient way I know of to achieve that goal. Which is, admittedly, unfortunate.
You could also do:
template <typename T>
concept something = requires {
f(better_declval<T>());
};
It just depends on your perspective I guess, and how many times you need to use obj
.
Once you've seen this T(&)()
pattern used in the concept context, it's a familiar pattern, so it makes sense to just use it consistently.