运算符重载可以在没有引用的情况下工作吗?

根据 Bjarne Stroustrup 的说法,C++ 中引入了引用以支持运算符重载:

According to Bjarne Stroustrup, references were introduced into C++ to support operator overloading:

引入引用主要是为了支持运算符重载.

References were introduced primarily to support operator overloading.

C 按值传递每个函数参数,如果按值传递对象效率低下或不合适,用户可以传递指针.在使用运算符重载的情况下,此策略不起作用.在这种情况下,符号方便是必不可少的,因为如果对象很大,则不能期望用户插入地址运算符.例如:

C passes every function argument by value, and where passing an object by value would be inefficient or inappropriate the user can pass a pointer. This strategy doesn't work where operator overloading is used. In that case, notational convenience is essential because users cannot be expected to insert address-of operators if the objects are large. For example:

a = b - c;

是可以接受的(即常规的)符号,但是

is acceptable (that is, conventional) notation, but

a = &b - &c;

不是.无论如何,&b - &c 在 C 中已经有了意义,我不想改变它.

is not. Anyway, &b - &c already has a meaning in C, and I didn't want to change that.

在语言中同时拥有指针和引用是 C++ 新手经常感到困惑的根源.

Having both pointers and references in the language is a constant source of confusion for C++ novices.

Bjarne 难道不能通过引入一种特殊的语言规则来解决这个问题,如果存在接受此类指针的用户定义的运算符函数,该规则允许对象参数衰减为指针吗?

Couldn't Bjarne have solved this problem by introducing a special language rule that allowed object arguments to decay into pointers if a user-defined operator function exists that takes such pointers?

减法的声明和用法看起来像这样:

The declaration and usage of subtraction would then have looked like:

Foo operator-(const Foo* x, const Foo* y);
a = b - c;

是否曾提出/考虑过这样的解决方案?它会有什么严重的缺点吗?

Was such a solution ever proposed/considered? Would there be any serious downsides to it?

是的,我知道,参考文献由于其限制而提供其他优势,但这不是重点.

Yes I know, references provide other advantages due to their restrictions, but that's not the point.

有趣的是,带有类的 C 的赋值运算符似乎是这样工作的:

Interestingly, the assignment operator of C with classes seems to have worked exactly like that:

通过声明一个名为 operator= 的类成员函数来改变类 [...] 对象赋值的含义.例如:

Changing the meaning of assignment for objects of a class [...] is done by declaring a class member function called operator=. For example:

class x {
public:
    int a;
    class y * p;
    void operator = (class x *);
};

推荐答案

我不明白这如何解决问题:operator- 调用指针已经有意义.

I don't see how this solves the problem: operator- called on pointers already has a meaning.

您将为 Foo* 参数定义一个 operator-,但它已经存在.

You'd be defining an operator- for Foo* arguments, but that already exists.

然后你需要一些人为的语义,当用指针显式调用时,调用指针算术运算符.当用对象左值调用时,它们会衰减为指针,然后调用重载版本".说实话,这似乎比添加引用要人为得多,也更不直观.

Then you'd need some contrived semantics that "when called explicitly with pointers, the pointer arithmetic operator is called. When called with object lvalues, they decay into pointers, and then the overloaded version is called". Which, to be honest, seems far more contrived, and much less intuitive than just adding references.

然后在 operator- 中,我将参数作为指针获取,然后如果我需要从那里调用另一个(或相同的)运算符,我最好确保取消引用这些参数.否则我会不小心最终执行指针运算.

And then inside the operator-, I get the arguments as pointers, and then I'd better make sure to dereference those if I need to call another (or the same) operator from there. Otherwise I'd accidentally end up performing pointer arithmetics.

您提议进行隐式转换,该转换的语义与您自己进行转换的语义不同.即:

You're proposing an implicit conversion which has different semantics than doing the conversion yourself. That is:

Foo foo;
bar(foo); 

执行从 FooFoo* 的隐式转换,然后调用签名为 void bar(Foo*) 的函数.

performs an implicit conversion from Foo to Foo*, and then a function with the signature void bar(Foo*) is called.

但是这段代码会做一些完全不同的事情:

But this code would do something completely different:

Foo foo;
bar(&foo);

那将也转换为Foo*,并且它还会调用一个带有签名void bar(Foo*)的函数,但它可能是一个不同的功能.(例如,在 operator- 的情况下,一个是用户重载的运算符,另一个是标准的指针算术运算符.

that would also convert to a Foo*, and it would also call a function with the signature void bar(Foo*), but it would potentially be a different function. (In the case of operator-, for example, one would be the user-overloaded operator, and the other would be the standard pointer arithmetic one.

然后考虑模板代码.一般来说,隐式转换使模板非常痛苦,因为传入的类型可能不是您想要操作的类型.

And then consider template code. In general, implicit conversions make templates really painful because the type passed in might not be the type you want to operate on.

当然,还有更多实际问题:

Of course, there are more practical problems too:

引用可以实现指针无法实现的优化.(编译器可以假设它们永远不会为空,并且可能能够更好地进行混叠分析)

References enable optimizations that aren't possible with pointers. (The compiler can assume they're never null, and it may be better able to do aliasing analysis)

此外,如果找不到匹配的 operator-,代码将静默失败.编译器不会告诉我那个,而是隐式地开始进行指针运算.不是破坏交易,但我真的很喜欢在可能的情况下尽早发现错误.

Moreover, code would fail silently if no matching operator- can be found. Instead of telling me that, the compiler would implicitly start doing pointer arithmetics. Not a deal-breaker, but I really really prefer errors to be caught early where possible.

C++ 的目标之一是使其通用:避免使用魔法"类型或函数.在现实世界的 C++ 中,运算符只是具有不同语法的函数.有了这个规则,它们也会有不同的语义.(如果用函数语法调用运算符呢?operator+(a, b)?

And one of the goals with C++ was to make it generic: to avoid having "magic" types or functions. In real-world C++, operators are very much just functions with a different syntax. With this rule, they would have different semantics as well. (What if the operator is called with function syntax? operator+(a, b)?

相关文章