【发布时间】:2021-11-19 23:03:50
【问题描述】:
C++ 模板 - 完整指南第 2 版在第 436 页有以下脚注(我的粗体):
除了
decltype(<i>call-expression</i>)不需要非引用、非void返回类型是完整的,这与其他上下文中的调用表达式不同。改用decltype(std::declval<T>().begin(), 0)确实增加了调用的返回类型是完整的要求,因为返回的值不再是decltype操作数的结果。
脚注指的是使用decltype(std::declval<T>().begin())(无效,基于脚注)来测试在T 上调用.begin() 是否有效。使用它的代码如下(为了清楚起见,它周围有一些文本:
诀窍是制定表达式来检查我们是否可以在
decltype表达式中调用begin()以获得附加函数模板参数的默认值:#include <utility> // for declval #include <type_traits> // for true_type, false_type, and void_t // primary template: template<typename, typename = std::void_t<>> struct HasBeginT : std::false_type {}; // partial specialization (may be SFINAE’d away): template<typename T> struct HasBeginT<T, std::void_t<decltype(std::declval<T>().begin())>> : std::true_type { };在这里,我们使用
decltype(std::declval<T>().begin())来测试给定T类型的值/对象(使用std::declval以避免需要任何构造函数),调用成员begin()是否有效。
从this previous question of mine了解到,既然operator,可以被重载,那么, 0的作用就是触发否则不存在的重载解析,而这又需要std::declval<T>().begin()的类型是完整的.
但是,书中的文字(参见上面粗体突出显示的部分)没有提到operator,,也没有提到重载决议。这只是糟糕的措辞吗?或者也许只是从不同的角度看待同一件事?还是什么?
【问题讨论】:
-
begin()意味着要返回一个迭代器,并且迭代器具有相当熟悉的预期 API,因此对于该示例,假设不会有重载的逗号运算符是模糊合理的。不过,在一般情况下,您的观点是合理的。 -
HolyBlackCat 解释了我在评论 cigien 的答案时的心态。但我会修改它。类型不必仅仅因为重载就完整。显然,对于函数调用(例如重载),参数必须是完整的(初始化和反初始化是调用的一部分)。但即使在没有重载的情况下,逗号运算符也会评估并丢弃 lhs。对于类纯右值,这涉及到实现一个临时值,因此我们再次进行初始化和反初始化,并需要一个完整的类型。
-
我认为您误解了原始源材料在说什么,但我不能确定,因为您只添加了一个脚注,我不知道它在说什么。 “在
T上调用begin()是有价值的”在 C++ 中的含义是模糊的,有多种合理的方法可以将英语中的“有效”一词映射到 C++ 中的不同概念。我不知道decltype在哪里,也不知道目标是什么。 -
@Yakk-AdamNevraumont,我是否添加了足够的信息?
标签: c++ templates c++17 decltype comma-operator