在驳斥了内置运算符不参与重载决策的概念之后,我正在仔细阅读第13.5节,并注意到没有任何部分operator->*
.它只是一个通用的二元运算符.
它的弟兄operator->
,operator*
和operator[]
,都要求有非静态成员函数.这排除了通常用于从对象获得引用的操作符的自由函数重载的定义.但不常见的operator->*
是遗漏了.
特别是,operator[]
有许多相似之处.它是二进制的(它们错过了使它成为n-ary的黄金机会),它接受左侧的某种容器和右侧的某种定位器.除了禁止免费功能外,其特殊规则部分13.5.5似乎没有任何实际效果.(这种限制甚至排除了对交换性的支持!)
因此,例如,这是完全合法的:
#include#include using namespace std; template< class T > T & operator->*( pair &l, bool r ) { return r? l.second : l.first; } template< class T > T & operator->*( bool l, pair &r ) { return r->*l; } int main() { pair y( 5, 6 ); y->*(0) = 7; y->*0->*y = 8; // evaluates to 7->*y = y.second cerr << y.first << " " << y.second << endl; }
它很容易找到用途,但替代语法往往不会那么糟糕.例如,缩放索引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
标准委员会是否忘记了这一点,它是否被认为太难看了,或者是否有现实世界的用例?
我所知道的最好的例子是Boost.Phoenix,它重载此运算符以实现惰性成员访问.
对于那些不熟悉Phoenix的人来说,它是一个极其漂亮的库,用于构建看起来像普通表达式的actor(或函数对象):
( arg1 % 2 == 1 ) // this expression evaluates to an actor
(3); // returns true since 3 % 2 == 1
// these actors can also be passed to standard algorithms:
std::find_if(c.begin(), c.end(), arg1 % 2 == 1);
// returns iterator to the first odd element of c
它通过重载operator%
和实现上述目标operator==
.- 应用于actor arg1
这些运算符返回另一个actor.可以以这种方式构建的表达式范围极端:
// print each element in c, noting its value relative to 5: std::for_each(c.begin(), c.end(), if_(arg1 > 5) [ cout << arg1 << " > 5\n" ] .else_ [ if_(arg1 == 5) [ cout << arg1 << " == 5\n" ] .else_ [ cout << arg1 << " < 5\n" ] ] );
在你使用Phoenix一段时间后(不是你曾经回去过),你会尝试这样的事情:
typedef std::vectorcontainer; container c; //... container::iterator inv = std::find_if(c.begin(), c.end(), arg1.ValidStateBit); std::cout << "A MyObj was invalid: " << inv->Id() << std::endl;
哪个会失败,因为凤凰城的演员当然没有会员ValidStateBit
.凤凰城通过超载解决这个问题operator->*
:
(arg1 ->* &MyObj::ValidStateBit) // evaluates to an actor (validMyObj); // returns true // used in your algorithm: container::iterator inv = std::find_if(c.begin(), c.end(), (arg1 ->* &MyObj::ValidStateBit) );
operator->*
的论点是:
LHS:演员回归MyObj *
RHS:会员的地址
它返回一个actor,它评估LHS并在其中查找指定的成员.(注意:你真的,真的想确保arg1
返回MyObj *
- 你没有看到一个大规模的模板错误,直到你在凤凰城出了问题.这个小程序产生了76,738个字符的痛苦(Boost 1.54,gcc 4.6):
#includeusing boost::phoenix::placeholders::arg1; struct C { int m; }; struct D { int n; }; int main() { ( arg1 ->* &D::n ) (new C); return 0; }
我同意你的看法标准是不一致的,它不允许operator[]
使用非成员函数进行重载并允许它operator->*
.我的观点operator[]
是数组和operator->*
结构/类(一个getter).使用索引选择数组的成员.使用成员指针选择结构的成员.
最糟糕的是我们可以尝试使用->*
而不是operator[]
像元素一样获取数组
int& operator->*(Array& lhs, int i); Array a; a ->* 2 = 10;
还有另一种可能的不连贯性.我们可以使用非成员函数来重载operator+=
和表单的所有运算符@=
),我们不能这样做operator=
.
我真的不知道使下列法律合法化的理由是什么
struct X { int val; explicit X(int i) : val(i) {} }; struct Z { int val; explicit Z(int i) : val(i) {} }; Z& operator+=(Z& lhs, const X& rhs) { lhs.val+=rhs.val; return lhs; } Z z(2); X x(3); z += x;
并且禁止
Z& operator=(Z& lhs, const X& rhs) { lhs.val=i; return lhs; } z = x;
很抱歉没有回答你的问题,但增加了更多的困惑.