declval<_Xp(&)()>()() - 这在下面的上下文中意味着什么?

Dav*_*ien 25 c++ type-traits c++20

这是来自: 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>; };
Run Code Online (Sandbox Code Playgroud)

我想弄清楚什么_Xp(&)()是 - 它是函数调用签名吗?即构造函数?确实没有意义。那里似乎有一个匿名变量名,即:

_Xp(&anon)()
Run Code Online (Sandbox Code Playgroud)

我仍然无法理解它,而且过去 34 年我一直在编写 C++ 代码。

任何解释表示赞赏。谢谢。

Bar*_*rry 31

太长;博士;我们需要一种方法来生成具有类型和值类别的表达式T,而不是类型和值类别T&&,因此我们不能只是使用std::declval<T>(),而是需要做其他事情。


这一点的要点是:

  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());
Run Code Online (Sandbox Code Playgroud)

是给你类型,false ? x : y其中x是类型和值类别的表达式_Xpy是类型和值类别的表达式_Yp

条件运算符(通常称为三元运算符)?:是一个极其复杂的语言特性。这是该语言中实际上区分纯右值和 xvalue 的地方之一。

实现这一点的简单方法是:

  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp>() : declval<_Yp>());
Run Code Online (Sandbox Code Playgroud)

因为,好吧,这不就是declval<T>()给你一个 吗T?但实际上,这里有一个缺陷,因为declval没有指定为:

template <typename T>
auto declval() -> T;
Run Code Online (Sandbox Code Playgroud)

它被指定为(add_rvalue_reference_t<T>而不是T&&正确处理void):

template <typename T>
auto declval() -> std::add_rvalue_reference_t<T>;
Run Code Online (Sandbox Code Playgroud)

结果,__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>());
Run Code Online (Sandbox Code Playgroud)

这有效。

另一种方法是生成一个函数实例T,然后调用它。这就是declval<_Xp(&)()>()()- 为您提供对返回 a 的空函数的引用_Xp,然后调用它,为您提供 an _Xp(正确的值类别)。

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

template <typename T>
concept something = requires (T(&obj)()){
    f(obj());
};
Run Code Online (Sandbox Code Playgroud)

在这里,我有一个概念,检查是否可以f使用 类型的表达式进行调用T,包括正确区分纯右值和 xvalues。以上是我所知道的实现该目标的最便捷方法。诚然,这是不幸的。

你还可以这样做:

template <typename T>
concept something = requires {
    f(better_declval<T>());
};
Run Code Online (Sandbox Code Playgroud)

我想这仅取决于您的观点,以及您需要使用多少次obj

一旦您在概念上下文中看到了此T(&)()模式的使用,就会发现它是一种熟悉的模式,因此一致地使用它是有意义的。

  • @DavidBien 就像给定的 `int foo();` 一样,`foo()` 的值类别是什么?这是一个纯右值。 (3认同)

Dre*_*ann 12

declval<_Xp(&)()>()() - 这是什么意思

_Xp(&)()是对不带参数的函数的引用。

declval<_Xp(&)()>()(含义std::decvlval)是该函数的假设实例。

declval<_Xp(&)()>()()正在调用该假设的实例,产生一个返回值。

总的来说,它的意思是“通过调用 类型的函数返回的值_Xp”。


Jan*_*tke 9

  • _Xp(&)()是对不带参数并返回 的函数的引用_Xp
  • declval<_Xp(&)()>()()返回这样的函数引用并调用它,这会导致_Xp.
decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()())
Run Code Online (Sandbox Code Playgroud)

_Xp... 是and的常见类型_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>; };
Run Code Online (Sandbox Code Playgroud)

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