我在refuting the notion之后仔细阅读了第13.5节,内置运算符没有参与重载解析,并注意到operator->*
上没有任何部分。它只是一个通用的二元运算符。
其兄弟operator->
,operator*
和operator[]
都必须是非静态成员函数。这排除了用于从对象获取引用的操作符通常的自由函数重载的定义。但是不常见的operator->*
被遗漏了。
特别是,operator[]
有许多相似之处。它是二进制的(它们错过了使它成为n-ary的黄金机会),它接受左侧的某种容器和右侧的某种定位器。除了禁止免费功能外,其特殊规则部分13.5.5似乎没有任何实际效果。 (而这种限制甚至排除了对交换性的支持!)
因此,例如,这是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;
}
很容易找到用途,但替代语法往往不会那么糟糕。例如,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
标准委员会是否忘记防止这种情况,是否认为太难看了,或是否存在真实世界的用例?
答案 0 :(得分:19)
我所知道的最好的例子是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::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->*
来解决这个问题:
(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->*
的论点是:
MyObj *
它返回一个actor,它评估LHS并在其中查找指定的成员。 (注意:你真的,真的希望确保arg1
返回MyObj *
- 在凤凰城出现问题之前你还没有看到大量的模板错误。这个小程序产生了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;
}
答案 1 :(得分:6)
我同意你的看法标准不一致,它不允许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;
很抱歉不回答你的问题,但更加混乱。
答案 2 :(得分:2)
谷歌搜索了一下,我发现有更多人询问是否使用过operator->*
而不是实际建议。
有几个地方建议T &A::operator->*( T B::* )
。不确定这是否反映了设计师的意图或T &A::operator->*( T A::* )
内置的错误印象。与我的问题没有关系,但是我对在线讨论中发现的深度有所了解。文献
有人提到“D&amp; E 11.5.4”,我想这是C ++的设计和演变。也许这包含一个提示。否则,我只是得出结论,标准化忽略了一些无用的丑陋,而且其他大多数人都忽略了。
修改请参阅下文,了解D&amp; E报价的粘贴。
为了定量地说明,->*
是可以通过自由函数重载的最紧密的绑定运算符。所有postfix-expression和一元运算符重载都需要非静态成员函数签名。一元运算符之后的下一个优先级是C样式转换,可以说对应于转换函数(operator type()
),它也不能是自由函数。然后是->*
,然后是乘法。 ->*
可能像[]
或类似%
,他们可能已经走了两条路,他们选择了 EEEEEEVIL 的路径
答案 3 :(得分:1)
标准(2010-02-16工作草案,第5.5节)说:
- &gt; *表达式的结果是 仅当第二个操作数是a时才值左值 指向数据成员的指针。如果是第二个 operand是指向成员的空指针 值(4.11),行为是 未定义。
您可能希望此行为明确定义。例如,检查它是否为空指针并处理这种情况。因此,我认为标准允许 - &gt; *重载是正确的决定。