当前位置:  开发笔记 > 编程语言 > 正文

是自由运营商 - >*超载邪恶?

如何解决《是自由运营商->*超载邪恶?》经验,为你挑选了2个好方法。

在驳斥了内置运算符不参与重载决策的概念之后,我正在仔细阅读第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

标准委员会是否忘记了这一点,它是否被认为太难看了,或者是否有现实世界的用例?



1> Kietz..:

我所知道的最好的例子是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 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->*的论点是:

LHS:演员回归MyObj *

RHS:会员的地址

它返回一个actor,它评估LHS并在其中查找指定的成员.(注意:你真的,真的想确保arg1返回MyObj *- 你没有看到一个大规模的模板错误,直到你在凤凰城出了问题.这个小程序产生了76,738个字符的痛苦(Boost 1.54,gcc 4.6):

#include 
using boost::phoenix::placeholders::arg1;

struct C { int m; };
struct D { int n; };

int main() {
  ( arg1  ->*  &D::n ) (new C);
  return 0;
}



2> Vicente Bote..:

我同意你的看法标准是不一致的,它不允许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;

很抱歉没有回答你的问题,但增加了更多的困惑.


不允许非成员`operator =`的理由是在D&E中.`operator =`是特殊的,如果你没有在类定义中声明自己的那个,编译器总是提供一个.如果允许非成员`operator =`,则可以在翻译单元的中间定义它.在这种情况下,位于*上面*定义的代码必须使用编译器提供的成员`operator =`和下面的代码**该定义必须使用显式定义的非成员`operator =` - 不一致.为了避免不一致,不允许使用独立的`operator =`.
§13.5.3不区分赋值运算符.它说"赋值运算符应由具有一个参数的非静态成员函数实现." §5.17说"有几个赋值运算符,所有这些运算符从右到左分组." 除非有我遗漏的东西,否则`operator =`不应该是特殊的.
@Potatoswatter:我记得它被缺陷报告所覆盖.这里实际上是:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#221.13.5.3仅用于指简单赋值.非成员函数允许复合赋值运算符重载,而简单赋值必须是成员.如此简单的赋值`operator =`很特别.
推荐阅读
臭小子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有