void()、逗号运算符 (operator,) 和不可能的 (?) 重载
Posted
技术标签:
【中文标题】void()、逗号运算符 (operator,) 和不可能的 (?) 重载【英文标题】:The void(), the comma operator (operator,) and the impossible (?) overloading 【发布时间】:2017-01-23 16:41:49 【问题描述】:考虑以下结构:
struct S ;
在 C++14 中,下面的定义是有效的:
constexpr auto f() return S, 'c';
还有下面这个:
constexpr auto f() return S, void();
现在,考虑以下涉及两个定义中的第一个的有效 sn-p:
#include<type_traits>
struct S ;
constexpr int operator,(S, char) return 42;
constexpr auto f() return S, 'c';
int main()
constexpr int if();
static_assert(i == 42, "!");
static_assert(std::is_same<decltype(f()), int>::value, "!");
从技术上讲,逗号运算符的重载拦截这对S, 'c'
并返回一个整数,正如在main
函数中正确验证的那样。
现在,假设我想对f
的第二个定义做同样的事情:
constexpr auto f() return S, void();
在这种情况下,逗号操作符应该截取S, void()
的形式。
以下定义都不起作用(原因很明显):
constexpr int operator,(S, void) return 42;
下面的也不是(在前一种情况下会起作用):
template<typename T> constexpr int operator,(S, T &&) return 42;
有没有什么办法可以重载逗号操作符来处理S, void()
?
否则不是标准中的缺失吗,因为它允许以这种方式使用逗号运算符,但不会让您有机会重载相同的运算符(即使the standard mentions that overloaded functions involving S
are allowed)?
注意:这个问题是出于好奇而提出的。请避免像 不要那样做或 这不是好的做法这样的 cmets。我不打算在生产环境中这样做。谢谢。
【问题讨论】:
这太疯狂了 ;) @JesperJuhl 是的,我知道。标准游击队。我正在探索语言中最晦涩的角落。 :-) 这真的是不可能的。强制转换为void
是防止人们通过重载迭代器类的逗号运算符来破坏您的循环(如 for(...;++it1, ++it2)
)的标准技巧。
@kennytm 哦,好的,我现在明白你的意思了。好吧,无论如何,请注意 1 + void()
无效,1, void()
是有效的。
@skypjack 是的,但是一些运算符在重载时会失去其特殊行为,例如&&
、||
不再短路。
【参考方案1】:
与此相关的子句是 N4140 中的 13.3.1.2/9 [over.match.oper]:
如果算子是算子
,
,一元算子&
,或者算子->
,并且没有可行的函数, 那么操作符被假定为内置操作符并根据第 5 条进行解释。
由于void()
从来都不是有效的函数参数(参见 5.2.2/7 [expr.call]),因此永远不存在可行的函数,因此将使用内置的 ,
。
所以不,您尝试做的事情是不可能的。
其实就是这样写一个迭代器循环
for(...; ++it1, (void)++it2)
是一种标准方法,通过强制使用内置运算符 ,
为迭代器类型重载 ,
,从而防止用户破坏您的代码。 (请注意,我并不是说您需要在日常代码中执行此操作。这在很大程度上取决于其实际使用情况。这是偏执狂的标准库级别。)
关于您链接的标准条款:
为每种类型预定义的运算符 =、(一元)& 和 ,(逗号)的含义可以针对特定的类和枚举类型进行更改通过定义实现这些运算符的运算符函数 .
但是不能定义这样的函数,因为正如我上面所说,void()
永远不是有效的函数参数。
现在这是否是标准中的疏忽/问题尚有待商榷。
【讨论】:
在循环中有趣地使用(void)
以避免调用重载的逗号。感谢您的提示!以上是关于void()、逗号运算符 (operator,) 和不可能的 (?) 重载的主要内容,如果未能解决你的问题,请参考以下文章
运行matlab时出现这个错误。是啥意思Error: Missing operator, comma, or semicolon.