为什么要定义运算符 +或者 += 在课堂之外,如何正确地做到这一点?
首选哪种方式有点困惑
Type operator + (const Type &type);
Type &operator += (const Type &type);
我对两者之间的差异以及
friend Type operator + (const Type &type1, const Type &type2);
friend Type &operator += (const Type &type1, const Type &type2);
,它们是什么样的以及何时应该使用?
I am a bit confused about the differences between
Type operator + (const Type &type);
Type &operator += (const Type &type);
and
friend Type operator + (const Type &type1, const Type &type2);
friend Type &operator += (const Type &type1, const Type &type2);
which way is preferred, what do they look like and when should either be used?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
运算符的第一种形式是您在类
Type
中定义的形式。运算符的第二种形式是您在与类
Type
相同的命名空间中定义的独立函数。定义独立函数是一个非常好的主意,因为这些函数的操作数可以参与隐式转换。
示例
假设此类:
您可以这样写:
但您不能这样写:
将
operator+
作为自由函数也允许最后一种情况。运算符的第二种形式的错误在于它通过直接调整其操作数的私有成员来执行加法(我假设是这样,否则它不需要成为朋友)。它不应该这样做:相反,运算符也应该在类内部定义,并且独立函数应该调用它们。
为了看看结果如何,让我们请求一位大师的服务:http://www. gotw.ca/gotw/004.htm。滚动到最后查看如何实现独立功能。
更新:
正如 James McNellis 在他的评论中指出的那样,给出的两种形式还有另一个区别:第一个版本中的左侧不是 const 限定的。由于
operator+
的操作数实际上不应该作为加法的一部分进行修改,因此始终对它们进行 const 限定是一个非常非常好的主意。我的示例中的 Type 类现在执行此操作,而最初它没有执行此操作。结论
处理运算符
+
和+=
的最佳方法是:operator+=
定义为T& T::operator+=(const T&);
在你的类中。这是实施添加的地方。operator+
定义为TT::operator+(const T&) const;
。该运算符将根据前一个运算符来实现。T operator+(const T&, const T&);
。该函数将调用成员operator+
来完成工作。您可以省略第 2 步并直接让自由函数调用
T::operator+=
,但根据个人喜好,我希望将所有加法逻辑保留在类中。The first form of the operators is what you would define inside class
Type
.The second form of the operators is what you would define as free-standing functions in the same namespace as class
Type
.It's a very good idea to define free-standing functions because then the operands to those can take part in implicit conversions.
Example
Assume this class:
You could then write:
But you could NOT write:
Having
operator+
as a free function allows the last case as well.What the second form of the operator does wrong though is that it performs the addition by directly tweaking the private members of its operands (I 'm assuming that, otherwise it would not need to be a friend). It should not be doing that: instead, the operators should also be defined inside the class and the free-standing functions should call them.
To see how that would turn out, let's ask for the services of a guru: http://www.gotw.ca/gotw/004.htm. Scroll at the very end to see how to implement the free-standing functions.
Update:
As James McNellis calls out in his comment, the two forms given also have another difference: the left-hand-side is not const-qualified in the first version. Since the operands of
operator+
should really not be modified as part of the addition, it's a very very good idea to const-qualify them all the time. The classType
in my example now does this, where initially it did not.Conclusion
The best way to deal with operators
+
and+=
is:operator+=
asT& T::operator+=(const T&);
inside your class. This is where the addition would be implemented.operator+
asT T::operator+(const T&) const;
inside your class. This operator would be implemented in terms of the previous one.T operator+(const T&, const T&);
outside the class, but inside the same namespace. This function would call the memberoperator+
to do the work.You can omit step 2 and have the free function call
T::operator+=
directly, but as a matter of personal preference I 'd want to keep all of the addition logic inside the class.相对于 C++03 和 C++0x 实现运算符的正确方法 (NRVO 和 move-semantics),是:
注意,很容易将上述内容合并为:
但有时(在我的测试中)编译器不会启用 NRVO(或移动语义)因为无法确定(直到它内联变异运算符)
first += secondary
与first
相同。更简单、更安全的方法是将其分开。The proper way to implement operators, with respect to C++03 and C++0x (NRVO and move-semantics), is:
Note it's tempting to combine the above into:
But sometimes (in my testing) the compiler doesn't enable NRVO (or move semantics) because it can't be certain (until it inlines the mutating operator) that
first += second
is the same asfirst
. Simpler and safer is to split it up.当所声明的事物不是类的成员,但需要访问类实例的私有成员才能完成其工作时,将使用
friend
说明符。如果您的运算符将在类本身中定义,请使用第一种方法;如果它是一个独立的函数,请使用第二个。
The
friend
specifier is used when the thing being declared isn't a member of the class, but needs access to the private members of the class's instances in order to do its job.If your operator will be defined in the class itself, use the first way; if it'll be a standalone function, use the second.
在许多情况下,您可以针对类中的类类型 T 编写运算符。主要是作为一个约定。然而,正如乔恩的回答所述,独立的函数可以帮助解决某些情况,而不需要更多的实现。
我还想提一下另一个重要的情况:存在一个类,但它不支持您想要的某个运算符。
这是一个示例:
系统没有定义该运算符,因此,如果您有许多函数在各处执行此类操作,那么能够将其添加到您自己的标头中并允许将 rhs 从 Unicode 字符自动转换为 UTF-8 是非常实用的。
在这种情况下,您别无选择,只能选择独立的函数,因为您不能只扩展您无法控制的现有类。
我的 libutf8 库中的功能示例:
https://github.com/m2osw/libutf8/斑点/c516aa954004cbbaca401e6aba3eb867fd329acd/libutf8/libutf8.h#L89-L132
In many cases, you write operators against a class type T within the class. Mostly as a convention. However, as noted in Jon's answer, free-standing functions can help in resolving certain cases without the need for more implementations.
There is also another important case I wanted to mention: a class exists and it does not support a certain operator that you would like to have.
Here is an example:
The system does not have that operator defined, so having the ability to add it in your own header and allowing the automatic conversion of the rhs from a Unicode character to UTF-8 is very practical if you have many functions doing such all over the place.
In this case, you do not have a choice but for a free-standing function since you can't just extend an existing class you do not control.
Functional examples in my libutf8 library:
https://github.com/m2osw/libutf8/blob/c516aa954004cbbaca401e6aba3eb867fd329acd/libutf8/libutf8.h#L89-L132