Pot*_*ter 38 c++ operator-overloading
在驳斥了内置运算符不参与重载决策的概念之后,我正在仔细阅读第13.5节,并注意到没有任何部分operator->*.它只是一个通用的二元运算符.
它的弟兄operator->,operator*和operator[],都要求有非静态成员函数.这排除了通常用于从对象获得引用的操作符的自由函数重载的定义.但不常见的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;
}
Run Code Online (Sandbox Code Playgroud)
它很容易找到用途,但替代语法往往不会那么糟糕.例如,缩放索引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
Run Code Online (Sandbox Code Playgroud)
标准委员会是否忘记了这一点,它是否被认为太难看了,或者是否有现实世界的用例?
Kie*_*etz 20
我所知道的最好的例子是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
Run Code Online (Sandbox Code Playgroud)
它通过重载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"
]
]
);
Run Code Online (Sandbox Code Playgroud)
在你使用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;
Run Code Online (Sandbox Code Playgroud)
哪个会失败,因为凤凰城的演员当然没有会员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) );
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
我同意你的看法标准是不一致的,它不允许operator[]使用非成员函数进行重载并允许它operator->*.我的观点operator[]是数组和operator->*结构/类(一个getter).使用索引选择数组的成员.使用成员指针选择结构的成员.
最糟糕的是我们可以尝试使用->*而不是operator[]像元素一样获取数组
int& operator->*(Array& lhs, int i);
Array a;
a ->* 2 = 10;
Run Code Online (Sandbox Code Playgroud)
还有另一种可能的不连贯性.我们可以使用非成员函数来重载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;
Run Code Online (Sandbox Code Playgroud)
并且禁止
Z& operator=(Z& lhs, const X& rhs) {
lhs.val=i;
return lhs;
}
z = x;
Run Code Online (Sandbox Code Playgroud)
很抱歉没有回答你的问题,但增加了更多的困惑.
谷歌搜索了一下,我发现人们询问是否operator->*曾经使用过的例子比实际建议的例子要多。
有几个地方建议T &A::operator->*( T B::* )。不确定这是否反映了设计师的意图,或者T &A::operator->*( T A::* )是内在的错误印象。与我的问题并不真正相关,但给出了我在在线讨论和文献中发现的深度的想法。
其中提到了“D&E 11.5.4”,我认为它是 C++ 的设计和演化。或许,这其中蕴含着某种暗示。否则,我会得出结论,这是一种被标准化所忽视的无用的丑陋,大多数其他人也是如此。
编辑请参阅下面的 D&E 报价粘贴。
定量地说,->*是可以由自由函数重载的最紧密的绑定运算符。所有后缀表达式和一元运算符重载都需要非静态成员函数签名。一元运算符之后的下一个优先级是 C 风格的强制转换,可以说它对应于转换函数 ( operator type()),但它也不能是自由函数。然后是->*,然后是乘法。->*可以是喜欢[]也可以是喜欢,他们可以选择任何一种方式,但他们选择了EEEEEEEVIL%的道路。
| 归档时间: |
|
| 查看次数: |
7848 次 |
| 最近记录: |