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

qlckcl4x  于 2023-07-01  发布在  其他
关注(0)|答案(3)|浏览(100)

这是来自: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++。
任何解释都是赞赏的。谢谢

qqrboqgw

qqrboqgw1#

tl;dr;我们需要一种方法来生成一个类型和值类别为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的表达式。
条件运算符(通常称为三元运算符)?:是一个非常复杂的语言特性。这是语言中的一个地方,实际上在纯右值和x值之间存在区别。
实现这一点的简单方法是:

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,包括正确区分纯右值和xvalues。以上是我所知道的实现这一目标的最简单的方法。当然,这很不幸。
您还可以:

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

我想这取决于你的观点,以及你需要使用obj多少次。
一旦您在概念上下文中看到了这个T(&)()模式,它就是一个熟悉的模式,因此一致地使用它是有意义的。

wgxvkvu9

wgxvkvu92#

declval<_Xp(&)()>()-这是什么意思
_Xp(&)()是对不带参数的函数的引用。
declval<_Xp(&)()>()(意思是std::decvlval)是这个函数的一个假设示例。
declval<_Xp(&)()>()()正在调用那个假设的示例,并产生一个返回值。
总的来说,它的意思是“通过调用_Xp类型的函数返回的值”。

kq4fsx7k

kq4fsx7k3#

  • _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++类型语法。*

相关问题