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

时间:2022-03-30 17:06:05

Consider the following struct:

考虑以下结构:

struct S {};

In C++14, the definition below is valid:

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

constexpr auto f() { return S{}, 'c'; }

As well as the following one:

以及以下一个:

constexpr auto f() { return S{}, void(); }

Now, consider the following, working snippet that involves the first of the two definitions:

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

#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, "!");
}

Speaking not so technically, the overload of the comma operator intercepts the couple S{}, 'c' and returns an integer, as correctly verified in the main function.

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

Now, suppose I want to do the same with the second definition of f:

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

constexpr auto f() { return S{}, void(); }

In this case, the comma operator should intercept the form S{}, void().
Neither the following definition works (for obvious reasons):

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

constexpr int operator,(S, void) { return 42; }

Nor the one below (that would have worked in the previous case):

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

template<typename T> constexpr int operator,(S, T &&) { return 42; }

Is there any way to overload the comma operator so as to deal with S{}, void()?
Isn't it otherwise a lack in the standard, for it allows to use the comma operator that way, but doesn't give you the chance to overload the same operator (even if the standard mentions that overloaded functions involving S are allowed)?

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


Note: this question is made for the sake of curiosity. Please, avoid comments like do not do that or it is not good practice. I'm not planning to do that in production environments. Thank you.

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

1 个解决方案

#1


22  

The relevant clause for this is 13.3.1.2/9 [over.match.oper] in N4140:

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

If the operator is the operator ,, the unary operator &, or the operator ->, and there are no viable functions, then the operator is assumed to be the built-in operator and interpreted according to Clause 5.

如果运算符是运算符,一元运算符&或运算符 - >,并且没有可行的函数,则假定运算符是内置运算符并根据第5条进行解释。

As void() is never a valid function argument (see 5.2.2/7 [expr.call]), there never is a viable function and thus the built-in , will be used.

由于void()永远不是有效的函数参数(参见5.2.2 / 7 [expr.call]),因此永远不会有可行的函数,因此将使用内置函数。

So no, what you are trying to do is not possible.

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

In fact, writing an iterator loop like this

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

for(...; ++it1, (void)++it2)

is a standard way to prevent users from breaking your code by overloading , for their iterator types by enforcing the built-in operator , to be used. (Note that I am not saying you need to do this in your everyday code. It very much depends on its actual use. This is standard library level of paranoid.)

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


Regarding the standard clause you linked:

关于您链接的标准条款:

The meaning of the operators =, (unary) &, and , (comma), predefined for each type, can be changed for specific class and enumeration types by defining operator functions that implement these operators.

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

But such a function cannot be defined because, as I said above, void() is never a valid function argument.

但是无法定义这样的函数,因为正如我上面所说,void()永远不是有效的函数参数。

Now whether or not this is an oversight/problem in the standard is open to debate.

现在,无论这是否是标准中的疏忽/问题,都可以进行辩论。

#1


22  

The relevant clause for this is 13.3.1.2/9 [over.match.oper] in N4140:

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

If the operator is the operator ,, the unary operator &, or the operator ->, and there are no viable functions, then the operator is assumed to be the built-in operator and interpreted according to Clause 5.

如果运算符是运算符,一元运算符&或运算符 - >,并且没有可行的函数,则假定运算符是内置运算符并根据第5条进行解释。

As void() is never a valid function argument (see 5.2.2/7 [expr.call]), there never is a viable function and thus the built-in , will be used.

由于void()永远不是有效的函数参数(参见5.2.2 / 7 [expr.call]),因此永远不会有可行的函数,因此将使用内置函数。

So no, what you are trying to do is not possible.

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

In fact, writing an iterator loop like this

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

for(...; ++it1, (void)++it2)

is a standard way to prevent users from breaking your code by overloading , for their iterator types by enforcing the built-in operator , to be used. (Note that I am not saying you need to do this in your everyday code. It very much depends on its actual use. This is standard library level of paranoid.)

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


Regarding the standard clause you linked:

关于您链接的标准条款:

The meaning of the operators =, (unary) &, and , (comma), predefined for each type, can be changed for specific class and enumeration types by defining operator functions that implement these operators.

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

But such a function cannot be defined because, as I said above, void() is never a valid function argument.

但是无法定义这样的函数,因为正如我上面所说,void()永远不是有效的函数参数。

Now whether or not this is an oversight/problem in the standard is open to debate.

现在,无论这是否是标准中的疏忽/问题,都可以进行辩论。