declval<_Xp(&)()>()() - 在下面的上下文中,这句话是什么意思?

25

这个链接来自于: 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>; };

我正在试图弄清楚_Xp(&)()是什么 - 它是一个函数调用签名吗?也就是说,它是一个构造函数吗?这真的没有意义。看起来那里有一个匿名变量名,例如:
_Xp(&anon)()

我还是无法理解它,尽管我过去34年一直在编写C++代码。
非常感谢任何解释。谢谢。

1
这是一种类型:一个引用不带参数并返回 _Xp 的函数。 - Sam Varshavchik
1
与函数指针_Xp(*)()的类型非常相似,只是使用引用而不是指针... - fabian
1
与函数指针_Xp(*)()的类型非常相似,只是使用引用而不是指针... - fabian
1
@SamVarshavchik(以及所有人):我的想法是,这是一种语法糖,用于生成由返回_Xp的值的方法所表示的任何C++值类别。作为一个附带问题:C++的返回值类别是什么?它不是右值引用,我查看了https://en.cppreference.com/w/cpp/language/value_category,但我无法轻松地弄清楚。 - David Bien
1
@SamVarshavchik(以及所有人):我的想法是,这是一种语法糖,用于生成由方法通过值返回的_Xp所表示的任何C++值类别。作为一个附带问题:C++中的返回值类别是什么?它不是右值引用,我查看了https://en.cppreference.com/w/cpp/language/value_category,但我无法轻易弄清楚。 - David Bien
显示剩余5条评论
3个回答

31
我们需要一种方法来生成具有类型和值类别 T 而不是类型和值类别 T&& 的表达式,所以我们不能只使用 std::declval<T>(),而是需要做一些其他的事情。
这个的目的是什么:
  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());

是为了给你提供类型为false ? x : y的内容,其中x是一个类型和值类别为_Xp的表达式,而y是一个类型和值类别为_Yp的表达式。

条件运算符(通常称为三元运算符)?:是一种非常复杂的语言特性。这是语言中实际上存在prvalues和xvalues之间区分的地方之一。

实现这个的朴素方法会是:

  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp>() : declval<_Yp>());

因为,嗯,这不就是 declval<T>() 的作用吗,给你一个 T 吗?但实际上,这里有一个缺陷,因为 declval 没有被指定为:
template <typename T>
auto declval() -> T;

这里指定为(add_rvalue_reference_t<T> 而不是 T&&),以正确处理 void

template <typename T>
auto declval() -> std::add_rvalue_reference_t<T>;

作为结果,__cond_res<int, int>__cond_res<int&&, int&&> 将无法区分,即使前者需要是 int 而后者需要是 int&&
所以,我们需要一种方法来生成一个任意类型为 T 的表达式。一种方法是直接:
template <typename T>
auto better_declval() -> T;

template<typename _Xp, typename _Yp>
  using __cond_res
    = decltype(false ? better_declval<_Xp>() : better_declval<_Yp>());

这个有效。

另一种方法是生成一个给你T的函数实例,然后调用它。这就是declval<_Xp(&)()>()()的作用 - 给你一个返回_Xp的无参数函数的引用,然后调用它,得到一个_Xp(具有正确的值类别)。

在这种情况下,与better_declval方法相比,这似乎是不必要的复杂性,但事实证明,这种模式在其他上下文中也很有用。比如概念:

template <typename T>
concept something = requires (T(&obj)()){
    f(obj());
};

这里,我有一个概念,用来检查是否可以使用类型为T的表达式调用f,并正确区分prvalues和xvalues。上述方法是我知道的实现这个目标最方便的方式。虽然这是不幸的。你也可以这样做:
template <typename T>
concept something = requires {
    f(better_declval<T>());
};

我想这只是取决于你的观点,以及你需要使用obj的次数。

一旦你在概念上看到了T(&)()这种模式的使用,它就会变得很熟悉,所以保持一致地使用它是有道理的。


再问一次:C++中的按值返回是属于哪个值类别?我在查看https://en.cppreference.com/w/cpp/language/value_category时没有轻易找到答案。 - David Bien
再问一次:C++中的按值返回是属于哪个值类别?我在查看https://en.cppreference.com/w/cpp/language/value_category时没有轻易找到答案。 - David Bien
3
@DavidBien 像给定的 int foo();foo() 的值类别是什么?它是一个 prvalue。 - Barry
3
@DavidBien 像给定的 int foo();foo() 的值类别是什么?它是一个 prvalue。 - Barry

12
declval<_Xp(&&)()>()() - 这是什么意思
_Xp(&&)() 是一个不带参数的函数的引用。
declval<_Xp(&&)()>(即std::declval)是这个函数的假设实例。
declval<_Xp(&&)()>()() 是调用该假设实例,产生一个返回值。
总体而言,它的意思是“调用类型为_Xp的函数所返回的值”。

1
“调用类型为 _Xp 的函数将返回的值。”我想你是指类型为 _Xp() 的函数吗? - ildjarn
1
调用类型为_Xp()的函数将返回的值。 - ildjarn
1
调用类型为_Xp的函数将返回的值。您是不是指_Xp()类型的函数? - undefined

9
  • _Xp(&)()是指不带参数并返回_Xp的函数的引用。
  • declval<_Xp(&)()>()()返回这样一个函数引用并调用它,结果为_Xp
decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()())

... 是一种常见的 `_Xp` 和 `_Yp` 类型,遵循条件运算符的规则。
与仅使用 `declval<_Xp>()` 的区别在于,`declval` 不返回一个值,而是返回 `std::add_rvalue_reference_t<_Xp>`。
您可以看到这个类型别名被用来确定两种类型之间的公共引用。
template<typename _Tp1, typename _Tp2>
struct __common_reference_impl<_Tp1, _Tp2, 3, void_t<__cond_res<_Tp1, _Tp2>>>
{ using type = __cond_res<_Tp1, _Tp2>; };

注意:您可以使用 cdecl+ 更好地理解 C 和 C++ 的类型语法。


declval&lt;_Xp&gt;()(如果_Xp不是void类型)永远不会成为一个纯右值(prvalue),而始终是一个将要被移动的右值引用(xvalue)或左值引用(lvalue)。因此,您简化版本的实现会增加很多右值引用,而实际上应该只是普通值。此外,这个检查还包括了不能作为函数返回类型的数组/函数(例如,common_reference&lt;int[3], int[3]&gt;int*,而不是int(&amp;)[3]int(&amp;&amp;)[3],以及common_reference&lt;int(), int()&gt;int(*)(),而不是int(&amp;)())。 - Artyer
@Artyer 如我上面所评论的:我的想法是,这是一种语法糖,用于生成由方法通过值返回_Xp所表示的任何C++值类别。作为一个附带问题:C++中的返回值类别是什么?它不是右值引用,我查看了en.cppreference.com/w/cpp/language/value_category,但很难弄清楚。 - David Bien
@DavidBien 当一个函数返回一个值时,调用它是一个 prvalue 表达式。返回引用会产生 lvalues 或 xvalues,取决于引用的类型。 - Jan Schultke
1
@JanSchultke 谢谢 - 这是cppref中关于prvalue的第一个解释 - 我读过了,但是一开始没太理解,现在明白了。 - David Bien
1
@JanSchultke 谢谢 - 这是在cppref中关于prvalue的第一个解释 - 我读过了,但是一开始没太理解,现在明白了。 - David Bien
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接