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.
4
4 Answers
4
Highest score (default)
Trending (recent votes count more)
Date modified (newest first)
Date created (oldest first)
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.
2
-
Asking this question again: What value category is a C++ return-by-value? I couldn't easily figure it out by looking at: en.cppreference.com/w/cpp/language/value_category
– David Bienyesterday
-
3
@DavidBien Like given
int foo();
, what is the value category offoo()
? It's a prvalue.– Barryyesterday
declval<_Xp(&)()>()() – what does this mean
_Xp(&)()
is a reference to a function that takes no parameters.
declval<_Xp(&)()>()
(meaning std::decvlval
) is a hypothetical instance of this function.
declval<_Xp(&)()>()()
is calling that hypothetical instance, producing a return value.
Collectively, it means "The value that would be returned by calling a function of type _Xp
."
1
-
1
"The value that would be returned by calling a function of type
_Xp
." I think you mean of type_Xp()
?– ildjarn19 hours ago
_Xp(&)()
is a reference to a function taking no parameters and returning_Xp
.declval<_Xp(&)()>()()
returns such a function reference and invokes it, which results in_Xp
.
decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()())
… is a common type of _Xp
and _Yp
, following the rules of the conditional operator.
The difference to just using declval<_Xp>()
is that declval
doesn’t return a value, it returns std::add_rvalue_reference_t<_Xp>
.
You can see that this type alias is being used to determine the common reference between two types:
template<typename _Tp1, typename _Tp2>
struct __common_reference_impl<_Tp1, _Tp2, 3, void_t<__cond_res<_Tp1, _Tp2>>>
{ using type = __cond_res<_Tp1, _Tp2>; };
Note: you can use cdecl+ to better understand C and C++ type syntax.
4
-
declval<_Xp>()
(if_Xp
isn't void) will never be a prvalue, always an xvalue or lvalue, so your simplified version would add a lot of rvalue references when it should just be values. Also this checks for arrays/functions that can't be the return type of a function (e.g.,common_reference<int[3], int[3]>
isint*
, notint(&)[3]
orint(&&)[3]
, andcommon_reference<int(), int()>
isint(*)()
, notint(&)()
)– Artyeryesterday
-
@Artyer As I commented above: My thought is that this is syntactic sugar to generate whatever C++ value category is represented by a method returning by value an _Xp. As a side question: What value category is a C++ return-by-value? It's not an rvalue-reference and I looked at en.cppreference.com/w/cpp/language/value_category and I couldn't easily figure it out.
– David Bienyesterday
-
@DavidBien when a function returns a value, calling it is a prvalue expression. Returning references produces lvalues or xvalues depending on the kind of reference.
– Jan Schultkeyesterday
-
1
@JanSchultke thanks – it's first explanation under prvalue in cppref – I read it and somehow it didn't grok but now it does.
– David Bienyesterday
_Xp(&)()
is a reference to a function that takes no arguments and has a return type of _Xp
. Here’s how you can confirm that:
#include <type_traits>
using Xp = int;
Xp foo() { return 0; }
int main() {
auto& refFoo = foo; // Deduced type
static_assert(std::is_same_v<decltype(refFoo), Xp (&)()>);
Xp (&refFoo2)() = foo; // Explicitly specified type
static_assert(std::is_same_v<decltype(refFoo2), Xp (&)()>);
}
Your Answer
Post as a guest
Required, but never shown
Post as a guest
Required, but never shown
By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.
Not the answer you're looking for? Browse other questions tagged
or ask your own question.
or ask your own question.
I would interpret it as function type declarations, where
_Xp
and_Yp
are template type parameters, the ampersands indicate that they are references to functions of the type_Xp
or_Yp
, and()
means that they have no input arguments.yesterday
It's a type: a reference to a function that takes no parameters and returns an
_Xp
.yesterday
Pretty similar to the type of a function pointer
_Xp(*)()
; just using a reference, not a pointer…yesterday
@SamVarshavchik (and all): My thought is that this is syntactic sugar to generate whatever C++ value category is represented by a method returning by value an _Xp. As a side question: What value category is a C++ return-by-value? It's not an rvalue-reference and I looked at en.cppreference.com/w/cpp/language/value_category and I couldn't easily figure it out.
yesterday
|