void(),逗号运算符(运算符)和不可能(?)重载

sky*_*ack 18 c++ operator-overloading void comma-operator language-lawyer

考虑以下结构:

struct S {};
Run Code Online (Sandbox Code Playgroud)

在C++ 14中,以下定义是有效的:

constexpr auto f() { return S{}, 'c'; }
Run Code Online (Sandbox Code Playgroud)

以及以下一个:

constexpr auto f() { return S{}, void(); }
Run Code Online (Sandbox Code Playgroud)

现在,请考虑以下涉及两个定义中的第一个的工作代码段:

#include<type_traits>

struct S {};

constexpr int operator,(S, char) { return 42; }
constexpr auto f() { return S{}, 'c'; }

int main() {
    constexpr int i{f()};
    static_assert(i == 42, "!");
    static_assert(std::is_same<decltype(f()), int>::value, "!");
}
Run Code Online (Sandbox Code Playgroud)

说来不是技术上的,逗号运算符的重载拦截了这对,S{}, 'c'并返回一个整数,正如在main函数中正确验证的那样.

现在,假设我想对第二个定义做同样的事情f:

constexpr auto f() { return S{}, void(); }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,逗号运算符应截取表单S{}, void().
以下定义都不起作用(出于显而易见的原因):

constexpr int operator,(S, void) { return 42; }
Run Code Online (Sandbox Code Playgroud)

也不是下面的那个(在前一个案例中会起作用):

template<typename T> constexpr int operator,(S, T &&) { return 42; }
Run Code Online (Sandbox Code Playgroud)

有没有办法重载逗号运算符以便处理S{}, void()
是否缺少标准,因为它允许以这种方式使用逗号运算符,但是不会让你有机会重载相同的运算符(即使标准提到S允许重载函数)?


注意:这个问题是为了好奇而制作的.请避免评论,如不这样做不是好习惯.我不打算在生产环境中这样做.谢谢.

Bau*_*gen 24

相关条款是N4140中的13.3.1.2/9 [over.match.oper]:

如果操作员是操作员,,一元操作员&或操作员->,并且没有可行的功能,则假定操作员是内置操作员并根据第5章解释.

因为void()永远不是一个有效的函数参数(见5.2.2/7 [expr.call]),所以从来没有一个可行的函数,因此,将使用内置函数.

所以不,你想做的事情是不可能的.

实际上,像这样编写迭代器循环

for(...; ++it1, (void)++it2)
Run Code Online (Sandbox Code Playgroud)

是一种标准方法,,通过强制使用内置运算符,来防止用户通过重载迭代器类型来破坏代码.(请注意,我并不是说您需要在日常代码中执行此操作.这在很大程度上取决于其实际使用情况.这是偏执狂的标准库级别.)


关于您链接的标准条款:

通过定义实现这些运算符的运算符函数,可以针对特定类和枚举类型更改为每种类型预定义的operator =,(一元)&和(逗号)的含义.

但是,无法定义这样的函数,因为如上所述,void()它永远不是有效的函数参数.

现在,无论这是否是标准中的疏忽/问题,都值得商榷.

  • 在循环上有趣地使用`(void)`来避免调用重载的逗号.谢谢你的提示! (3认同)