Non-friend operator+ with two parameters in Wandevoorde&Jossutis' book - c++

Learning Expression templates.
In Wandevoode and Jossutis's book Templates, the complete guide, section 18.2.3 The Operators, they define an operator+ with two arguments but not as a friend method.
template <typename T, typename R1, typename R2>
Array<T,A_Add<T,R1,R2> >
operator+ (Array<T,R1> const& a, Array<T,R2> const& b) {
return Array<T,A_Add<T,R1,R2> >
(A_Add<T,R1,R2>(a.rep(),b.rep()));
}
I am a beginner and hence insecure about what I know about C++.
Shouldn't this operator+ be a friend?
I tried
class A{
public:
explicit A(int x) : a(x){};
A& operator+(const A& x, const A& y){
A temp{x.a + y.a};
return temp;
};
private:
int a;
};
I am getting a binary 'operator+' has too many parameters.

Your "binary" operator + member function gives you the "too many parameters" error because it has three parameters: the x and y you specified, plus the implicit this parameter. This means it actually takes three arguments, which of course is impossible.
You can make a member function taking one explicit parameter and the implicit this, but in the opinion of many people, these binary operators such as add and subtract are better implemented outside the class, in the same namespace as the class, as free functions taking two explicit parameters. This works especially well if the data needed to fulfill the operation can be publicly accessed (otherwise you must use friend, which is OK if truly necessary).

To summarize for the next reader.
There seem to be three forms of overloading binary arithmetic operators.
First: A member method taking one parameter by const reference. The const tothe return value is used (according to this reference) to avoid (x+y)=z.
class A{
const A operator+(const A& other){
// ...
};
};
Second: A friend method, and therefore included in the definition of the class, taking two parameters.
class A{
friend A& operator+(const A& operand1, const A& operand2){
// ...
};
};
Third: A non-member method taking two parameters.
class A{
// ...
};
A operator+(const A& operand1, const A& operand2){
// ...
};

Related

Systematic approach of how to think about operator overloading

I'm learning how to do operator overloading and trying to come up with a systematic approach of how to know number of arguments an overloaded operator should take in and if the function should be constant.
I know the system is not perfect and doesn't catch all the edge cases, but I'm thinking that will work itself out with some logical thinking in the end.
What I'm looking for is if this systematic approach is catching the majority of cases, or am I missing something important?
Systematic approach
Important: Think logically through the entire process regarding if the input paramters can be const, * or &.
Is the operator overloader a member function and NOT a stream?* It should take 1 argument which is the value/object to the right of the operator, UNLESS it is a unary operator then it should take 0 arguments.
Does it alter the class object? If it does, add const to the end of it.
Is the operator overloader a member function and ALSO a stream?* It should take 2 arguments, first a reference to the stream object and secondly (most likely) a reference to the class object.
Does it alter the class object? If it does, add const to the end of it.
Is the operator overloader NOT a member function?* In that case it should take 2 arguments which is the value/object to the left and right of the operator, UNLESS it is a unary operator then it should take 1 argument. No const at the end needed.
Below is the header file that I based the system on.
Vector.h
#pragma once
#include <iostream>
class Vector
{
public:
Vector(double x = 0.0, double y = 0.0);
Vector& operator+=(Vector const& other);
Vector& operator-=(Vector const& other);
Vector& operator*=(double other);
Vector& operator/=(double other);
Vector operator-() const;
bool operator==(Vector const& other) const;
bool operator!=(Vector const& other) const;
double operator*(Vector const& rhs) const;
double length() const;
friend std::ostream& operator<<(std::ostream& os, Vector const& other);
friend std::istream& operator>>(std::istream& is, Vector& other);
private:
double x;
double y;
};
Vector operator+(Vector const& lhs, Vector const& rhs);
Vector operator-(Vector const& lhs, Vector const& rhs);
Vector operator*(Vector const& lhs, double rhs);
Vector operator*(double lhs, Vector const& rhs);
Vector operator/(Vector const& lhs, double rhs);
You got one thing wrong:
friend std::ostream& operator<<(std::ostream& os, Vector const& other);
You say this is written with two explicit arguments "because one of them is a stream." But actually it takes two explicit arguments because the friend keyword on the front implies this is a free function, not a member function. So it's covered by your third rule, and your second rule should be deleted entirely.
Its both less and more diverse than you sketched.
More diverse because your systematic approach misses some operators. operator() and, since C++23, operator[] can have arbitrary number of arguments. ++ and -- have an argument merely to distinguish between post and pre de/increment.
Less diverse, because your confusion seems to be mainly related to operators declared as friends defined inside the class definition. And friend / free function or member and number of arguments isn't really specific to operator overloading.
Whether a free function is a friend and defined in the class definition has some impact on ADL. Though, operators as friends are not much different than other free functions as friends. For example:
struct foo {
friend void do_something(foo) {}
};
struct bar {};
void do_something(bar) {}
int main() {
do_something(foo{});
do_something(bar{});
}
Two classes, foo and bar, and for both there is a free function called do_something. It is important to note that the do_something defined in foo is not a member of foo. I may look rather similar to a member function definition but it is none. ADL aside (which is the reason we can actually call do_something(foo{})), the only reason we would prefer making do_something a friend is when it needs access to private members. And thats the same for operator overloads.
Now consider the difference between a free function and a member function:
struct moo {
int x;
void print_x() const { std::cout << x; }
};
void print_x(const moo& m) { std::cout << m.x; }
They both do exactly the same thing. Both need an object to be called. One is called like this m.print_x(); the other is called like this print_x(m);.
Ergo, the number of parameters does not depend on whether it is a friend of the class. A member function needs one argument less than a free function that does the same.
For the rest I refer you to https://en.cppreference.com/w/cpp/language/operators. Most importantly it has a list that tells you (among other things) which operators can be overloaded as free function and which operators can only be overloaded as members. For those that can be free functions you need to decide: Free function or member. If you go for free function and it needs access to private members, you must make it a friend.
Overloads for std::ostreams << are somewhat special. Or actually not that special, you just need to consider that you are overloading an operator of std::ostream not one of your class. Because you cannot add a member to std::ostream such overloads can only be free functions.
Regarding const the usual applies. If it can be const, make it const.

Which takes precedence, the free function operator==() or the member function operator==()?

When the free operator function and the member operator function are both defined, which one is used for comparison?
#include <iostream>
class A;
class B;
bool operator==(const A &a, const B &b){ return true; };
class A
{
public:
bool operator==( const B &rhs ){ return false; };
};
class B
{
};
int main( int argc, char **argv )
{
A a;
B b;
if( a == b )
{
std::cout << "a==b" << std::endl;
}
};
I ran the code a couple times and it seems that the member operator wins out. Is this always the case?
I ran the code a couple times and it seems that the member operator wins out.
Yes, the member wins in your example. But not for the reason you think.
Member functions have an implicit object parameter (what this points to), and the type of the object parameter is determined by the cv-qualifiers at the end of the member function. In this case, your member operator has no cv-qualifiers, so the type of the implicit object is simply A.
Basically, we have two candidates:
bool operator==(A const&, B const&); // your non-member operator
bool operator==(A&, B const&); // your member operator
The member operator is a better match because the first parameter is a better match - we don't have to take a more const-qualified reference to a.
If you had made your operator const (as you generally should), then we'd have these two candidates:
bool operator==(A const&, B const&); // your non-member operator
bool operator==(A const&, B const&); // better member operator
Which are identical, there is no reason to prefer one to the other, and we'd get an ambiguity. There is no rule to prefer a member to a non-member function (or vice versa).
You're declaring the 1st parameter type as const & in the non-member operator. Then given a == b, for the non-member operator to be called a needs to be converted to const. On the other hand the member operator is declared as non-const, then its operand is a non-const A then it's an exact match and wins in overload resolution.
If you change the 1st parameter type of the non-member operator to A& you'll get an ambiguity error. LIVE
Or qualify the member operator as const you'll get the ambiguity error too. LIVE

Operator overloading for primitive types in C++

In C++, if you've got a class Foo, and you want users of Foo to be able to write:
Foo x;
x += 3;
, you can simply make a member function of Foo, Foo& operator+=(int rhs). If you want to be able to write:
Foo x;
int y = 3;
y += x;
, you cannot accomplish this by writing a member function of Foo, instead one has to write an external function, which usually must be declared as a friend of Foo.
How hard would it be for a future version of C++ to say that this can be written with a Foo member function int& operator+=(int &lhs, Reversed), where Reversed was some empty class whose sole purpose was to distinguish the two versions of the operator?
If this were done, it could probably eliminate the vast majority of the uses of the friend keyword in new code.
You can in fact define such an operator, because you are free to overload += for built-in types:
int& operator+=(int &lhs, Foo &rhs) {
lhs += rhs.somefield;
return lhs;
}
On the other hand, instead of writing overloaded functions for all possible operators, you can also provide a function that will allow implicit casts of class Foo to int:
class Foo {
... somefield;
operator int() {
return (int)somefield;
}
};

Non-friend single-line operator + overload based on operator +=

Let's say I have a class:
class A
{
//...
};
With well defined operator +=:
A& operator +=(const A&) {return *this;} //class without members
So let's try to overload operator+ also (as non-friend). I don't want to use class name to call constructor of temporary object (kinda want this make generic code):
A operator +(const A& other) const
{
return auto(*this)(*this) += other; //error: invalid use of auto
// /\/\/\/\/\ /\
// type of *this ||
// constructor call
}
auto is no good here. Let's try decltype.
A operator +(const A& other) const
{
return decltype(*this)(*this) += other; //error: 'A& A::operator+=(const A&)' discards
// /\/\/\/\/\/\/\ /\ qualifiers [-fpermissive] return
// type of *this || decltype(*this)(*this) += other;
// constructor call ^
}
This managed to get the type out of *this, but operator+ is declared const, so we got const A deduced (that's what I thought). So let's go on:
A operator +(const A& other) const
{
return typename std::remove_const<decltype(*this)>::type(*this) += amount;
//same error as previous
}
Now I got mindblown. Even thought I removed constness, it still discards
qualifier. Well, maybe that's because all I was doing was just CASTING. So stupid. To call a constructor I would have to generate code that (besides type) has ::Constructor (so I even tried to make an alias for constructor, but at this point I failed so hard). My broken brain gave up, but rest of my consciousness gave me an solution (which is generic in writing, so that's good):
// outside of class
template<class A>
inline A&& make_rvalue(A copy)
{
return std::move(copy);
}
// inside of class
A operator +(const A& other) const
{
return make_rvalue(*this) += other; // such generic code
}
That's what I ended with. But is there some trick that doesn't involve any other function call?
EDIT: I know classic methods of doing this, but what I search is described below:
operator is reduced to {return /*...*/;}
doesn't involve names of class methods or global functions
takes to account overloads with other types - it cannot take argument(s) by value, since class A != class B, argument int over const int& doesn't help much with Matrix class (but proxy operator that calls target operator with exchanged arguments is OK)
takes to account (possible) order of operation (x#y != y#x), where both should should have same return statement
return statement should be exacly the same for given operator# is every class that has overloaded operator +=
If you create a function like this:
template <typename T>
T add(T temp,const T &b)
{
temp += b;
return temp;
}
You can use it like this:
A operator+(const A& other) const { return add(*this,other); }
I'm going to go on record as saying this whole line of thinking/coding is going the wrong way. You already know that (in this case) the return type is A. Your code seems to be putting a lot of work into saying A in the most complex, indirect way possible.
A operator +(const A& other) const {
A ret {*this};
ret += other;
return ret;
}
Since you seem to really want to use C++11 features (even though in this case almost none of them provides any real help) I used a braced initializer, so it uses something from C++11. And no, this isn't one line, but it is readable.
Note that the same style is fine in generic code as well. For example, in a non-member overload of operator+, we might have something like this:
template <class T>
T operator+(T const &a, T const &b) {
T ret {a};
ret += b;
return ret;
}
This can be simplified a bit as well though:
template <class T>
T operator+(T ret, t const &b) {
ret += b;
return ret;
}
Again, we lose precisely nothing from the fact that older compilers can and will accept the code without problem.

Why must (), [], ->, and = be overloaded only as member functions? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I tried to analyse why some of the operators ((), [], ->, =) should be overloaded as member functions only. I failed and I tried to search on internet but of no use. Can any one please help me to understand this restriction?
I think this is most likely why that portion of the standard was written that way.
but if it is not forbidden, the friend version would never be called, in my testing code ,when
Complex operator+(const Complex &other);
is defined as private, the compiler would
give error message
‘Complex Complex::operator+(const Complex&)’ is private
Complex Complex::operator+(const Complex &other)
instead of using the friend version
refer to Why cannot a non-member function be used for overloading the assignment operator?
Because the default operator= provided by the compiler (the memberwise copy one) would always take precedence. I.e. your friend operator= would never be called.
(If the assignment was performed inside a class method, because of the lookup rules, the member function (in this case generated by the compiler) would take precedence)
I try to using operator + for test. it prove the precedence
it outputs:
member function called
7+11i
testing code:
#include<iostream>
using namespace std;
class Complex
{
public:
Complex(int real, int imag);
Complex(void);
~Complex(void);
Complex &Add(const Complex &other);
void Display() const;
Complex operator+(const Complex &other);
friend Complex operator+(const Complex &c1, const Complex &c2);
private:
int real_;
int imag_;
};
Complex::Complex(int real, int imag): imag_(imag), real_(real)
{
}
Complex::Complex(void)
{
}
Complex::~Complex(void)
{
}
Complex &Complex::Add(const Complex &other)
{
real_ += other.real_;
imag_ += other.imag_;
return *this;
}
void Complex::Display() const
{
cout << real_ << "+" << imag_ << "i" << endl;
}
Complex Complex::operator+(const Complex &other)
{
int r = real_ + other.real_;
int i = imag_ + other.imag_;
std::cout << "member function called"<< std::endl;
return Complex(r, i);
}
Complex operator+(const Complex &c1, const Complex &c2)
{
int r = c1.real_ + c2.real_;
int i = c1.imag_ + c2.imag_;
std::cout << "friend function called"<<std::endl;
return Complex(r, i);
}
int main(void)
{
Complex c1(3, 5);
Complex c2(4, 6);
Complex c3 = c1 + c2;
c3.Display();
return 0;
}
Certain forms of operators require access to the "this" pointer of an instance of a class in order to operate directly on that instance as they only take a single argument to the same type.
class A{
public:
int x;
// only takes a reference to another 'A' type, so we need the 'this' pointer
A& operator=(const A& other){
this->x = other.x;
return *this;
}
};
Although maybe not very satisfying, here's what the standard has to say about it:
(emphasis is mine)
§3.5.3 Assignment
An assignment operator shall be implemented by a non-static member function with exactly one parameter.
§3.5.4 Function call
operator() shall be a non-static member function with an arbitrary number of parameters.
§3.5.5 Subscripting
operator[] shall be a non-static member function with exactly one parameter.
§3.5.6 Class member acess
operator-> shall be a non-static member function taking no parameters.
I can't speak to the reasoning behind these requirements, so I will leave that aspect to those that are more familiar with how compilers are designed.