我们需要一种方法来生成具有类型和值类别
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(&)()
这种模式的使用,它就会变得很熟悉,所以保持一致地使用它是有道理的。
_Xp
的函数。 - Sam Varshavchik_Xp(*)()
的类型非常相似,只是使用引用而不是指针... - fabian_Xp(*)()
的类型非常相似,只是使用引用而不是指针... - fabian