Why define operator + or += outside a class, and how to do it properly?
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:
class Type {
public:
Type(int foo) { }
// Added the const qualifier as an update: see end of answer
Type operator + (const Type& type) const { return *this; }
};
You could then write:
Type a = Type(1) + Type(2); // OK
Type b = Type(1) + 2; // Also OK: conversion of int(2) to Type
But you could NOT write:
Type c = 1 + Type(2); // DOES NOT COMPILE
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 class Type
in my example now does this, where initially it did not.
Conclusion
The best way to deal with operators +
and +=
is:
- Define
operator+=
asT& T::operator+=(const T&);
inside your class. This is where the addition would be implemented. - Define
operator+
asT T::operator+(const T&) const;
inside your class. This operator would be implemented in terms of the previous one. - Provide a free function
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.
Operator overloading outside class
The basic question is "Do you want conversions to be performed on the left-hand side parameter of an operator?". If yes, use a free function. If no, use a class member.
For example, for operator+()
for strings, we want conversions to be performed so we can say things like:
string a = "bar";
string b = "foo" + a;
where a conversion is performed to turn the char * "foo"
into an std::string
. So, we make operator+()
for strings into a free function.
operator overloading outside of a class!
I suspect you have the definition in a different namespace than the declaration. ADL is finding the declaration (since it's in the same namespace as the class), and then you get an unresolved external error during link.
e.g.
-foo.h-
namespace aspace
{
class A
{
public:
A(float x);
float x;
};
A operator +(const A&, const A&);
}
-foo.cpp-
#include "foo.h"
using namespace aspace;
A::A(float x)
{
this->x = x;
}
A operator +(const A& lh, const A& rh)
{
return A(lh.x + rh.x);
}
Will give the error you describe. The solution is to put the operator+
definition in the correct namespace:
namespace aspace
{
A operator +(const A& lh, const A& rh)
{
return A(lh.x + rh.x);
}
}
Overload + Operator for Class Data
To answer your first 2 questions
It is recommended (but not enforced) to use the
friend
approach for binary operators because it allows to have objects that are convertible to your class on the left hand side of the operator. Consider this:class Foo
{
// private members
public:
Foo(int) {/* ctor implementation */} // implicit convertible to int
Foo(const Foo&) { /* copy ctor implementation */ }
friend Foo operator+(const Foo& lhs, const Foo& rhs){/* implementation */}
};Now you can do:
Foo foo1{1};
Foo foo2 = 1 + foo1; // 1 is implicitly converted to Foo hereIf
operator+
would have been a member function, the call above would fail (left hand side, even if convertible toFoo
, will not be automatically converted by the compiler), and you'd be only able to useFoo foo2 = foo1 + 1;
So, in conclusion, using
friend
for binary operators make them more "symmetric".Last question:
The assembler would not generate the same code, as the friend/member
operator+
are doing slightly different things.
See also a very good guide about overloading operators here.
Can comparison operator be defaulted outside of class definition in C++20?
P2085R0 removed the requirement on the defaulted comparison operator to be defaulted on the first declaration. Clang currently doesn't support this proposal:
See also https://reviews.llvm.org/D103929
C# Overloading operator== outside the class
No, it's not possible to override an operator from a class that is not involved in the operation.
You can make a class that implements IEualityComparer<SomeClass>
, which can be used instead of the standard comparison in some cases, for example in a dictionary:
var x = new Dictionary<SomeClass, string>(new SomeClassEqualityComparer());
If you just want to use the comparison in your own class, you could make it a regular static method instead of overriding an operator:
public static bool SomeClassEqual(SomeClass a, SomeClass b) {
if (a.i == b.i) {
return true;
}
// compare some other members as well
return false;
}
Usage:
if (SomeClassEqual(a, b))
C++ Defining the << operator of an inner class
Since the friend operator is first declared inside the class, it's only available by argument-dependent lookup. However, neither of its parameter types are in namespace A
, so it won't be found. C
is an alias for std::map
, so is considered to be in namespace std
for the purposes of ADL.
There are various ways you could fix it, none of which are perfect:
- Declare the function in
namespace A
before the class definition; then it becomes available by normal lookup, not just ADL. However, this breaks the encapsulation somewhat, and might cause problems if anything else tries to overloadoperator<<
forstd::map
. - Replace the operator overload with a named static (not friend) function, and call it by name.
- Declare
C
as an inner class, rather than an alias forstd::map
. This enables ADL without breaking encapsulation, but is a bit awkward if you want it to behave just likestd::map
.
Related Topics
Automatic Copy Files to Output During Application Building
Alternative to C++ Static Virtual Methods
Opencv - Dll Missing, But It's Not
Search 25 000 Words Within a Text
Can You Use Keyword Explicit to Prevent Automatic Conversion of Method Parameters
What Is the Use of Volatile Keyword
C++ Remove_If on a Vector of Objects
Pointers as Keys in Map C++ Stl
Opencv Image Loading for Opengl Texture
In C++, Differencebetween a Method and a Function
Noexcept, Stack Unwinding and Performance
Reference to Non-Static Member Function Must Be Called
What's the Point of Std::Unique_Ptr::Get
Typecasting Eigen::Vectorxd to Std::Vector
C++ Array of Pointers: Delete or Delete []