void()、逗号运算符 (operator,) 和不可能的 (?) 重载
The void(), the comma operator (operator,) and the impossible (?) overloading
考虑以下结构:
struct S {};
在 C++14 中,以下定义有效:
constexpr auto f() { return S{}, 'c'; }
以及以下一项:
constexpr auto f() { return S{}, void(); }
现在,考虑以下涉及两个定义中的第一个的工作片段:
#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, "!");
}
从技术上讲,逗号运算符 的重载截取 这对 S{}, 'c'
和 returns 一个整数,如 [= 中正确验证的那样18=]函数。
现在,假设我想对 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)?
注:本题为好奇而生。请避免评论不要那样做或这不是好的做法。我不打算在生产环境中这样做。谢谢。
与此相关的条款是 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.
因为 void()
永远不是一个有效的函数参数(见 5.2.2/7 [expr.call]),所以从来没有一个可行的函数,因此内置 ,
将被使用。
所以不,你想做的事是不可能的。
其实就是这样写迭代器循环
for(...; ++it1, (void)++it2)
是防止用户通过强制使用内置运算符 ,
为其迭代器类型重载 ,
来破坏您的代码的标准方法。 (请注意,我并不是说你需要在日常代码中这样做。这在很大程度上取决于它的实际使用。这是偏执狂的标准库级别。)
关于您链接的标准条款:
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.
但是无法定义这样的函数,因为正如我上面所说,void()
永远不是有效的函数参数。
现在这是否是标准中的 oversight/problem 尚待讨论。
考虑以下结构:
struct S {};
在 C++14 中,以下定义有效:
constexpr auto f() { return S{}, 'c'; }
以及以下一项:
constexpr auto f() { return S{}, void(); }
现在,考虑以下涉及两个定义中的第一个的工作片段:
#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, "!");
}
从技术上讲,逗号运算符 的重载截取 这对 S{}, 'c'
和 returns 一个整数,如 [= 中正确验证的那样18=]函数。
现在,假设我想对 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)?
注:本题为好奇而生。请避免评论不要那样做或这不是好的做法。我不打算在生产环境中这样做。谢谢。
与此相关的条款是 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.
因为 void()
永远不是一个有效的函数参数(见 5.2.2/7 [expr.call]),所以从来没有一个可行的函数,因此内置 ,
将被使用。
所以不,你想做的事是不可能的。
其实就是这样写迭代器循环
for(...; ++it1, (void)++it2)
是防止用户通过强制使用内置运算符 ,
为其迭代器类型重载 ,
来破坏您的代码的标准方法。 (请注意,我并不是说你需要在日常代码中这样做。这在很大程度上取决于它的实际使用。这是偏执狂的标准库级别。)
关于您链接的标准条款:
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.
但是无法定义这样的函数,因为正如我上面所说,void()
永远不是有效的函数参数。
现在这是否是标准中的 oversight/problem 尚待讨论。