自由运营商 – > *超负荷的邪恶?

我在反驳内build运算符不参与重载parsing的概念后,正在细读13.5节,注意到operator->*没有任何部分。 它只是一个通用的二元运算符。

它的弟弟, operator->operator*operator[]都是非静态成员函数。 这排除了通常用于从对象获得参考的操作员的自由function过载的定义。 但不常见的operator->*被省略。

特别是operator[]有许多相似之处。 它是二进制的(他们错过了一个进行n-ary的黄金机会),它接受左边的某种容器和右边的某种定位器。 除了禁止自由职能外,其特殊规则第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; } 

这很容易find用途,但替代语法往往不是那么糟糕。 例如, 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 ,它重载这个运算符来实现懒惰的成员访问。

对于那些不熟悉凤凰城的人来说,它是一个非常漂亮的图书馆,用于构build看似正常expression式的演员(或函数对象):

 ( 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==来实现上述目的。 – 应用于演员arg1这些操作员返回另一个演员。 可以用这种方式build立的expression范围是极端的:

 // 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" ] ] ); 

在你使用凤凰卫士一段时间后(不是你回头),你会尝试这样的事情:

 typedef std::vector<MyObj> container; 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->*解决这个问题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:会员的地址

它返回一个评估LHS并查找指定成员的actor。 (注意:你真的确实想要确保arg1返回MyObj * – 你没有看到一个巨大的模板错误,直到你在Phoenix中出错为止。这个小程序产生76,738个字符的疼痛(Boost 1.54,gcc 4.6):

 #include <boost/phoenix.hpp> using 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)。 数组的成员使用索引进行select。 结构的成员是使用成员指针select的。

最糟糕的是,我们可以试图使用->*而不是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; 

抱歉不回答你的问题,但增加更多的困惑。

search一下,我发现更多的人询问operator->*是否被使用过,而不是实际的build议。

几个地方build议T &A::operator->*( TB::* ) 。 不确定这是否反映出devise者的意图或T &A::operator->*( TA::* )是内build的错误印象。 与我的问题没有什么关系,但是给出了我在网上讨论和文献中find的深度。

有一个提到“D&E 11.5.4”,我认为是C ++的devise和进化。 也许这包含一个提示。 否则,我只能断定这是一个被标准化忽视的无用的丑陋,而且也是大多数其他人。

编辑见下面的D&E报价贴。

定量地说, ->*是可以通过自由函数重载的最紧密的绑定运算符。 所有后缀expression式和一元运算符重载都需要非静态成员函数签名。 一元运算符之后的下一个优先级是C型转换,可以说是对应于转换函数( operator type() ),这也不能是自由函数。 然后来->* ,然后乘法。 ->*本来可以像[]或者像%一样,他们本来可以不用 ,他们select了EEEEEEVIL的path。

标准(工作草案2010-02-16,第5.5节)说:

只有第二个操作数是指向数据成员的指针时, – > *expression式的结果才是左值。 如果第二个操作数是成员值(4.11)的空指针,则行为是未定义的。

你可能想要这个行为是明确的 。 例如,检查它是否为空指针并处理这种情况。 所以我认为这是一个标准允许 – > *重载是正确的决定。