自由运算符->* 重载是邪恶的吗?
在反驳这样的观点之后,我正在仔细阅读第13.5节:内置运算符不参与重载决策,并注意到没有关于 operator->*
的部分。它只是一个通用的二元运算符。
它的兄弟 operator->
、operator*
和 operator[]
都必须是非静态成员函数。这排除了对通常用于从对象获取引用的运算符的自由函数重载的定义。但不常见的 operator->*
被遗漏了。
特别是,operator[]
有许多相似之处。它是二进制的(他们错过了将其变成 n 进制的黄金机会),并且它接受左侧的某种容器和右侧的某种定位器。它的特殊规则部分 13.5.5 除了禁止自由函数之外似乎没有任何实际作用。 (这个限制甚至排除了对交换性的支持!)
因此,例如,这是完全合法:
#include <utility>
#include <iostream>
using namespace std;
template< class T >
T &
operator->*( pair<T,T> &l, bool r )
{ return r? l.second : l.first; }
template< class T >
T & operator->*( bool l, pair<T,T> &r ) { return r->*l; }
int main() {
pair<int, int> y( 5, 6 );
y->*(0) = 7;
y->*0->*y = 8; // evaluates to 7->*y = y.second
cerr << y.first << " " << y.second << endl;
}
很容易找到使用,但替代语法往往不会那么糟糕。例如,矢量的缩放索引:
v->*matrix_width[2][5] = x; // ->* not hopelessly out of place
my_indexer<2> m( v, dim ); // my_indexer being the type of (v->*width)
m[2][5] = x; // it is probably more practical to slice just once
标准委员会是否忘记阻止这种情况,是否被认为太难看而无需打扰,或者是否存在实际用例?
I was perusing section 13.5 after refuting the notion that built-in operators do not participate in overload resolution, and noticed that there is no section on operator->*
. It is just a generic binary operator.
Its brethren, operator->
, operator*
, and operator[]
, are all required to be non-static member functions. This precludes definition of a free function overload to an operator commonly used to obtain a reference from an object. But the uncommon operator->*
is left out.
In particular, operator[]
has many similarities. It is binary (they missed a golden opportunity to make it n-ary), and it accepts some kind of container on the left and some kind of locator on the right. Its special-rules section, 13.5.5, doesn't seem to have any actual effect except to outlaw free functions. (And that restriction even precludes support for commutativity!)
So, for example, this is perfectly legal:
#include <utility>
#include <iostream>
using namespace std;
template< class T >
T &
operator->*( pair<T,T> &l, bool r )
{ return r? l.second : l.first; }
template< class T >
T & operator->*( bool l, pair<T,T> &r ) { return r->*l; }
int main() {
pair<int, int> y( 5, 6 );
y->*(0) = 7;
y->*0->*y = 8; // evaluates to 7->*y = y.second
cerr << y.first << " " << y.second << endl;
}
It's easy to find uses, but alternative syntax tends not to be that bad. For example, scaled indexes for vector
:
v->*matrix_width[2][5] = x; // ->* not hopelessly out of place
my_indexer<2> m( v, dim ); // my_indexer being the type of (v->*width)
m[2][5] = x; // it is probably more practical to slice just once
Did the standards committee forget to prevent this, was it considered too ugly to bother, or are there real-world use cases?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我知道的最好的例子是 Boost.Phoenix,它重载此运算符以实现惰性成员访问。
对于那些不熟悉 Phoenix 的人来说,它是一个非常漂亮的库,用于构建看起来像普通表达式的参与者(或函数对象):
它通过重载
operator%
和operator==. - 应用于演员
arg1
这些运算符返回另一个演员。可以用这种方式构建的表达式的范围是极端的:在您使用 Phoenix 一段时间后(不是您再回去),您将尝试这样的事情:
这会失败,因为当然 Phoenix 的演员不这样做有一个成员
ValidStateBit
。 Phoenix 通过重载operator->*
来解决这个问题:operator->*
的参数是:MyObj *
它返回一个计算 LHS 并在其中查找指定成员的参与者。 (注意:您真的,真的想要确保
arg1
返回MyObj *
- 在得到一些东西之前,您还没有看到大规模的模板错误Phoenix 中的错误。这个小程序生成了 76,738 个令人痛苦的字符(Boost 1.54,gcc 4.6):The best example I am aware of is Boost.Phoenix, which overloads this operator to implement lazy member access.
For those unfamiliar with Phoenix, it is a supremely nifty library for building actors (or function objects) that look like normal expressions:
It achieves the above by overloading
operator%
andoperator==
. - applied to the actorarg1
these operators return another actor. The range of expressions which can be built in this manner is extreme:After you have been using Phoenix for a short while (not that you ever go back) you will try something like this:
Which will fail, because of course Phoenix's actors do not have a member
ValidStateBit
. Phoenix gets around this by overloadingoperator->*
:operator->*
's arguments are:MyObj *
It returns an actor which evaluates the LHS and looks for the specified member in it. (NB: You really, really want to make sure that
arg1
returnsMyObj *
- you have not seen a massive template error until you get something wrong in Phoenix. This little program generated 76,738 characters of pain (Boost 1.54, gcc 4.6):我同意你的观点,标准上存在不一致,它不允许使用非成员函数重载
operator[]
并允许operator->*
>。在我看来,operator[]
之于数组,就像operator->*
之于结构/类(getter)一样。使用索引选择数组的成员。使用成员指针选择结构体的成员。最糟糕的是,我们可能会尝试使用
->*
而不是operator[]
来获取类似数组的元素。还有另一种可能的不连贯性。我们可以使用非成员函数来重载
operator+=
和所有@=
形式的运算符),但我们不能对operator=
执行此操作。我真的不知道使以下内容合法化
和禁止的
理由是什么抱歉,不回答您的问题,但增加了更多的混乱。
I agree with you that there is an incoherence on the standard, It doesn't allows overloading of
operator[]
with non-member functions and allows it foroperator->*
. For my point of viewoperator[]
is to arrays asoperator->*
is to structs/classes (a getter). Members of an array are selected using an index. Members of a struct are selected using member pointers.The worst is that we can be tempted to use
->*
instead ofoperator[]
to get an array like elementThere is also another possible incoherence. We can use a non member function to overload
operator+=
and all the operator of the form@=
) and we cannot do it foroperator=
.I don't really know what is the rationale to make the the following legal
and forbidding
Sorry to not answer to your question, but adding even more confusion.
谷歌搜索了一下,我发现更多的人询问
operator->*
是否被使用过,而不是实际的建议。有几个地方建议
T &A::operator->*( TB::* )
。不确定这是否反映了设计者的意图,还是错误地认为T &A::operator->*( TA::* )
是内置函数。与我的问题并不真正相关,但给出了我在在线讨论和讨论中发现的深度的想法。文学。其中提到了“D&E 11.5.4”,我认为它是 C++ 的设计和演化。或许这其中蕴含着某种暗示。否则,我会得出结论,这是一种被标准化所忽视的无用的丑陋,大多数其他人也是如此。
编辑 请参阅下面的 D&E 报价粘贴。
定量地说,
->*
是可以由自由函数重载的最紧密的绑定运算符。所有后缀表达式和一元运算符重载都需要非静态成员函数签名。一元运算符之后的下一个优先级是 C 风格的强制转换,可以说它对应于转换函数(operator type()
),但它也不能是自由函数。然后是->*
,然后是乘法。->*
可以像[]
或%
一样,它们可以选择任何一种方式,并且它们选择了 EEEEEEVIL。Googling around a bit, I found more instances of people asking whether
operator->*
is ever used than actual suggestions.A couple places suggest
T &A::operator->*( T B::* )
. Not sure whether this reflects designer's intent or a misimpression thatT &A::operator->*( T A::* )
is a builtin. Not really related to my question, but gives an idea of the depth I found in online discussion & literature.There was a mention of "D&E 11.5.4" which I suppose is Design and Evolution of C++. Perhaps that contains a hint. Otherwise, I'm just gonna conclude it's a bit of useless ugliness that was overlooked by standardization, and most everyone else too.
Edit See below for a paste of the D&E quote.
To put this quantitatively,
->*
is the tightest binding operator that can be overloaded by a free function. All the postfix-expression and unary operators overloads require nonstatic member function signatures. Next precedence after unary operators are C-style casts, which could be said to correspond to conversion functions (operator type()
), which also cannot be free functions. Then comes->*
, then multiplication.->*
could have been like[]
or like%
, they could have gone either way, and they chose the path of EEEEEEVIL.标准(工作草案 2010-02-16,第 5.5 条)规定:
您可能希望此行为明确定义。例如,检查是否为空指针并处理这种情况。所以我认为标准允许 ->* 重载是正确的决定。
Standard (Working Draft 2010-02-16, § 5.5) says:
You may want this behavior to be well-defined. For example, check if it is a null pointer and handle this situation. SO I quess it is right decision for a standard to allow ->* overloading.