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 inside vs outside class
First consider this example using your class (reformatted to a style I prefer):
class A
{
public:
auto operator==( A const* p )
-> bool
{
return true; // Whatever.
}
};
auto main()
-> int
{
A u{}, v{};
A const c{};
bool const r1 = (u == &v); // OK but needlessly annoying.
bool const r2 = (c == &v); // !Will not compile.
}
Here
- Since the argument is a pointer, the client code must apply
&
. - Since the method is not
const
, objects that areconst
can not be compared.
The conventional way to do this is therefore to pass the argument by reference, and to make the method const
:
class B
{
public:
auto operator==( B const& o ) const
-> bool
{
return true; // Whatever.
}
};
auto main()
-> int
{
B u{}, v{};
B const c{};
bool const r1 = (u == v); // OK.
bool const r2 = (c == v); // OK.
}
If you define the comparison outside the class, like this:
class B
{};
auto operator==( B const& u, B const& v )
-> bool
{
return true; // Whatever.
}
auto main()
-> int
{
B u{}, v{};
B const c{};
bool const r1 = (u == v); // OK.
bool const r2 = (c == v); // OK.
}
… then
- The definition guaranteed does not depend on internal implementation details of class
B
, that conceivably could change in the future. - Also the first argument can be a value that converts implicitly to
B
. - You can, if you want, have formal arguments of different types, where the class only appears in the second argument, like this:
auto operator==( int const u, B const& v )
-> bool
{
return true; // Whatever.
}
If you choose to place these non-member operators inline inside the class definition, via the friend
mechanism (so that they're found via ADL lookup), then you lose the first bullet point's advantage, but you then have all the code relevant for use of the class, within the class definition:
class B
{
public:
friend
auto operator==( B const& u, B const& v )
-> bool
{
return true; // Whatever.
}
friend
auto operator==( int const u, B const& v )
-> bool
{
return true; // Whatever.
}
};
auto main()
-> int
{
B u{}, v{};
B const c{};
bool const r1 = (u == v); // OK.
bool const r2 = (c == v); // OK.
bool const r3 = (42 == v); // OK.
}
Operator overloading outside of a class in a namespace
You should define your operator==()
as inline
function if present in header file because you are very probably going to violate One Definition Rule.
http://en.cppreference.com/w/cpp/language/definition [One Definition Rule]
One and only one definition of every non-inline function or variable
that is odr-used (see below) is required to appear in the entire
program (including any standard and user-defined libraries). The
compiler is not required to diagnose this violation, but the behavior
of the program that violates it is undefined.For an inline function,
a definition is required in every translation unit where it is
odr-used.
Basically there are bug categories how to violate ODR.
- Multiple definitions in single compilation unit. That's why we are using include guards in header files.
- No violation on compilation unit level but multiple compilation units containing the definition that are linked together. (This case.)
Fix is trivial:
namespace DirectX{
namespace PackedVector{
inline bool operator==(const DirectX::PackedVector::XMUBYTEN4 & lhs, const DirectX::PackedVector::XMUBYTEN4 & rhs) {
if (lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w)
return true;
return false;
}
}
}
overloading operator outside of struct
The problem with
bool operator < (mystruct * n1, mystruct* n2) {
return n1 -> label < n2 -> label;
};
Is that both n1
and n2
are pointers. Even though they are pointers to mystruct
they are still just pointers and you cannot overload operators for pointers as they are built in. The simplest way to fix this is to take references instead and work with values like
bool operator < (const mystruct& n1, const mystruct7 n2) {
return n.label < n2.label;
};
int main() {
set<mystruct> s;
return 0;
}
If you can't do that then you need to provide a comparison functor to the std::set
and have it use that function instead of operator <
. That would look like
struct mystruct_pointer_comp
{
bool operator ()(mystruct * n1, mystruct* n2) {
return n1->label < n2->label;
};
}
int main() {
set<mystruct *, mystruct_pointer_comp> s;
return 0;
}
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);
}
}
Overloading and as outside member functions for template class
For starters the friend functions are not member functions of the class where they are decalred.
So these declarations in any case are incorrect.
template <typename Type>
std::ostream& myClass<Type> :: operator << (std::ostream& out, const myClass<Type>& other) { ... }
template <typename Type>
std::istream& myClass<Type> :: operator >> (std::istream& in, myClass<Type>& other) { ... }
Secondly within the class definition you declared non-template friend functions.
Either you need to provide their definitions within the class definition. Or for each potential template argument of the class you have to define the friend functions outside the class.
Here is a demonstration program that shows how to define a non-template friend function within the class template definition.
#include <iostream>
template <typename T>
class A
{
private:
T t;
public:
A( const T &t ) : t( t ) {}
friend std::ostream &operator <<( std::ostream &os, const A &a )
{
return os << a.t;
}
};
int main()
{
std::cout << A<int>( 10 ) << '\n';
std::cout << A<const char *>( "Hello" ) << '\n';
}
The program output is
10
Hello
And here is a demonstration program that shows how to define the friend functions outside the class template definition. That is for each used specialization of the class template you need to define the non-template friend functions.
#include <iostream>
template <typename T>
class A
{
private:
T t;
public:
A( const T &t ) : t( t ) {}
friend std::ostream &operator <<( std::ostream &os, const A &a );
};
std::ostream &operator <<( std::ostream &os, const A<int> &a )
{
return os << a.t;
}
std::ostream &operator <<( std::ostream &os, const A<const char *> &a )
{
return os << a.t;
}
int main()
{
std::cout << A<int>( 10 ) << '\n';
std::cout << A<const char *>( "Hello" ) << '\n';
}
An alternative approach is to declare template friend functions. For example
#include <iostream>
template <typename T>
class A
{
private:
T t;
public:
A( const T &t ) : t( t ) {}
template <typename T>
friend std::ostream &operator <<( std::ostream &os, const A<T> &a );
};
template <typename T>
std::ostream &operator <<( std::ostream &os, const A<T> &a )
{
return os << a.t;
}
int main()
{
std::cout << A<int>( 10 ) << '\n';
std::cout << A<const char *>( "Hello" ) << '\n';
}
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.
Related Topics
Best Method for Storing This Pointer for Use in Wndproc
Linking to Msvc Dll from Mingw
How Can Std::Make_Heap Be Implemented While Making at Most 3N Comparisons
How to Correctly Initialize Member Variable of Template Type
Does Casting to an Int After Std::Floor Guarantee the Right Result
How Does a 32 Bit Processor Support 64 Bit Integers
Error with Address of Parenthesized Member Function
How to Convert a Char Array to a String
Is There Actually a Reason Why Overloaded && and || Don't Short Circuit
Why Does This Program Crash: Passing of Std::String Between Dlls
How to Check String Start in C++