Consider the following code:
class A
{
public:
A& operator=( const A& );
const A& operator+( const A& );
const A& operator+( int m );
};
int main()
{
A a;
a = ( a + a ) + 5; // error: binary '+' : no operator found which takes a left-hand operand of type 'const A'
}
Can anyone explain why the above is returned as an error?
"( a + a )" calls "const A& operator+( const A& )" and returns a constant reference which is then passed to "const A& operator+( int m )" if I'm not mistaken.
How can one fix the above error (without creating a global binary operator+ or a constructor that accepts an int) such that the statement inside main() is allowed?
which is then passed to "const A& operator+( int m )" if I'm not mistaken
No. Since the LHS is a const A& and RHS is an int, it will call*
[anyType] operator+ (int rhs) const
// ^^^^^ note the const here.
as you've only provided the non-const version const A& operator+( int m ), the compiler will complain.
*: Or operator+(const int& rhs) const or operator+(float rhs) const... The crucial point is that it must be a const method.
operator+ should return an instance, not a reference:
// as member function
A operator+(const A& other);
// as free function
A operator+(const A& left, const A& right);
To explain the specific problem is "returns a constant reference which is then passed to const A& operator+( int m )". Since you have a const reference, it cannot call that function because it's not a const method (i.e. const A& operator+( int m ) const).
That said, that is not the way to fix operator+. If you're returning a reference, what is it a reference to? A local in operator+ would be bad as you shouldn't return a reference to a local. A reference to a global would be bad because it will limit how your code can be used properly. A reference to allocated memory would be bad because it will leak memory. A reference to *this would be bad because then operator+ is acting like operator +=.
Because you are modifying the left-hand side object when adding. You can't do that with a const object. Take Samuel advice btw, because the idiomatic way is to return a new copy of the added objects.
The function needs to be const:
const A& operator+( int m ) const;
As const A& operator+( const A& ) returns a const reference a non-const member function const A& operator+( int m ) can not be called over a const object.
Either, the first operator should be defined as A& operator+( const A& ) or, the second operator as const A& operator+( int m )const;
However, these changes will only make them technically correct, not aesthetically in majority of the cases, as a binary operator is not supposed to modify any of the input argument and yet to compute a result and return. Thus the result have to be returned by value or in case of C++0x , as a r-value reference.
i.e A operator+(const A& rhs)const or A&& operator+(const A& rhs)const;
The problem is that (a+a) returns a so-called rvalue (basically a fancy term for a temporary). While you can invoke member functions on an rvalue, you can only invoke const member functions. Also, everyone is right in saying that operator+ must alwasys return a new value.
Your operators should be implemented like this:
A operator+( const A& ) const;
A operator+( int m ) const;
However, binary operators which don't modify their left argument are probably better implemented as free functions:
class A { ... };
A operator+(const A& lhs, const A& rhs);
A operator+(const A& lhs, int rhs);
Usually, they are implemented on top of operator+=, which is implemented as a member:
class A {
public:
A& operator+=(const A& rhs);
A& operator+=(int rhs);
};
inline A operator+(A lhs, const A& rhs) // note: lhs is passed by copy now
{
lhs += rhs;
return lhs;
}
A operator+(A lhs, int rhs) // note: lhs is passed by copy now
{
lhs += rhs;
return lhs;
}
Related
R-values appear to provide incomplete support for unnamed temporaries, or am I missing something here?
C++11 provides excellent support for rvalues to implement move semantics, useful for converting expensive allocate and copy cycles into fast and cheap moves in constant time, similar to moving a reference. But C++11 came late in the game, and by then I had a fully developed solution to the costly unnamed temporaries problem, using the class-based solution outlined below.
Only when I recently attempted to replace my solution with with "modern" C++11 move constructors, did I discover rvalue management doesn't cover important cases that the class-based solution covers. A representative example is the expression A + B. When A is an unnamed temporary (rvalue), an in-place implementation of A += B is appropriate, when A is not an unnamed temporary (lvalue), A + B computes a new result. But C+11 rvalue support appears to address only a right argument rvalue, but not a left rvalue.
By extension, this limitation affect all other operators and functions on the base type that could benefit from treating *this as an rvalue when appropriate. Note that A + B can even be computed as B += A, when A is an lvalue and B is an rvalue. The benefits from a complete solution can often be applied more to *this rvalues than to right argument rvalues. If C++11 provides only a half solution here, then the class-based solution below remains significantly superior for many things. Am I missing something here?
So let's derive an unnamed temporary T class from the class of S values, add appropriate constructors, assignments, operators and functions to S and T, then substitute T for S as the return type for all functions and operators that return an S result. With this, we get all the same move semantics as with rvalues, plus support for additional functions and operators that can operate faster on unnamed temporary values in-place.
class S { // S is a sample base type to extend
protected:
mutable char* p; // mutable pointer to storage
mutable int length; // mutable current length
mutable int size; // mutable current size
public:
~S ( ); // S destructor
S (char* s); // construct from data
S (const S& s); // from another S
S (const T& s); // construct from an unnamed temporary
T& result ( ) { return (T&)*this; } // cast *this into a T& (an equivalent to std::move (*this))
S& take (S& s); // free *this, move s to *this, put s in empty/valid state
S& operator= (const S& s); // copy s to *this
S& operator= (const T& s); // assign from unnamed temporary using take ( )
S& operator+= (const S& v); // add v to *this in-place
S& operator-= (const S& v); // subtract v from *this in-place
S& operator<<= (Integer shift); // shift *this in-place
S& operator>>= (Integer shift);
T operator+ (const S& v); // add v to *this and return a T
T operator- (const S& v); // subtract v from *this and return a T
etc...
};
class T : public S { // T is an unnamed temporary S
private:
T& operator= (const T& s); // no public assignments
void* operator new (size_t size); // don't define -- no heap allocation
void operator delete (void* ptr);
public:
T (char* s) : S (s) { }; // create a new temporary from data
T (const S& s) : S (s) { }; // copy a new temporary from a non-temporary
T (const T& s) : S (s) { }; // move a temporary to new temporary
T operator<< (int shift) const { return ((S&)*this <<= shift).result ( ); }
T operator>> (int shift) const { return ((S&)*this >>= shift).result ( ); }
T operator+ (const S& v) const { return ((S&)*this += v).result ( ); }
T operator- (const S& v) const { return ((S&)*this -= v).result ( ); }
};
Note that this method has demonstrated its correctness and effectiveness across a variety of comprehensive data types (including strings, arrays, large integers, etc) since 2001, so it works without any reference to C++11 features, and relies on no undefined language features.
You appear to be wrong in your assumptions. C++ supports rvalues on either the left or the right.
There are two ways to do this.
struct noisy {
noisy() { std::cout << "ctor()\n"; };
noisy(noisy const&) { std::cout << "ctor(const&)\n"; };
noisy(noisy &&) { std::cout << "ctor(&&)\n"; };
noisy& operator=(noisy const&) { std::cout << "asgn(const&)\n"; return *this; };
noisy& operator=(noisy &&) { std::cout << "asgn(&&)\n"; return *this; };
~noisy() { std::cout << "dtor\n"; };
};
struct Bob:noisy {
int val = 0;
Bob(int x=0):val(x) {}
Bob(Bob&&)=default;
Bob(Bob const&)=default;
Bob& operator=(Bob&&)=default;
Bob& operator=(Bob const&)=default;
friend Bob operator+( Bob lhs, Bob const& rhs ) {
lhs += rhs;
return lhs;
}
friend Bob& operator+=( Bob& lhs, Bob const& rhs ) {
lhs.val += rhs.val;
return lhs;
}
friend Bob operator+=( Bob&& lhs, Bob const& rhs ) {
lhs += rhs; // uses & overload above
return std::move(lhs);
}
};
Bob uses friend operators to do basically what you want.
This is my preferred solution, friend operators are far more symmetric than member operators are.
struct Alice:noisy {
int val = 0;
Alice(int x=0):val(x) {}
Alice(Alice&&)=default;
Alice(Alice const&)=default;
Alice& operator=(Alice&&)=default;
Alice& operator=(Alice const&)=default;
Alice operator+( Alice const& rhs ) const& {
return Alice(*this) + rhs;
}
Alice operator+( Alice const& rhs ) && {
*this += rhs;
return std::move(*this);
}
Alice& operator+=( Alice const& rhs )& {
val += rhs.val;
return *this;
}
Alice operator+=( Alice const& rhs )&& {
*this += rhs; // uses & overload above
return std::move(*this);
}
};
Alice uses member functions to do the same. It has the usual problems with member functions over friend operators.
Notice the use of & and && and const& after the member function arguments. This is known as the "rvalue reference to *this" feature in casual discussion. It lets you pick which overload based on the r/l value-ness of the object being worked with.
Test code:
Bob bob;
Bob b2 = Bob{3}+bob;
Alice alice;
Alice a2 = Alice{3}+alice;
Live example.
In neither case, no objects are copied.
Note that I assumed addition was assymetric (despite using an int for state). If it was, you can do another efficiency where the lhs is a non-rvalue while the rhs is.
class A {
public:
string operator+( const A& rhs ) {
return "this and A&";
}
};
string operator+( const A& lhs, const A& rhs ) {
return "A& and A&";
}
string operator-( const A& lhs, const A& rhs ) {
return "A& and A&";
}
int main() {
A a;
cout << "a+a = " << a + a << endl;
cout << "a-a = " << a - a << endl;
return 0;
}
//output
a+a = this and A&
a-a = A& and A&
I'm curious as to why the operator inside the class gets to be called rather than the outside one. Is there some sort of priority among operators?
The process of selecting amongst several functions of the same name is called overload resolution. In this code, the member is preferred because the non-member requires a qualification conversion (adding const to lhs) but the member does not. If you made the member function const (which you should, since it does not modify *this) then it would be ambiguous.
Whenever your object is non-const there's a priority of non-constness over constness. The inner will be called when the left side is non-const, and the outer will be called when the left side is const.
See what happens when the inner is defined as:
string operator+( const A& rhs ) const;
class Vector{
......
.......
private:
int dim;
public:
int getDim() {
return this->dim;
}
const Vector operator+(const Vector& right){
this->getSize();
}
};
And I got compile error in this->getSize();. It is caused fact, that argument right is const. I don't know where is problem. I don't try modify right.
Presumably you have a non-const method Vector::getSize(). You need to make it const so that it can be called on const objects or via const references or pointers to const. For example:
int getSize() const;
^^^^^
Also note that it doesn't make much sense to return a const value (and would inhibit move semantics if you had them). The canonical form of an addition member operator would be
// const method: A = B + C should not modify B
Vector operator+(const Vector& right) const;
^^^^^
and the non-member
Vector operator+(const Vector& left, const Vector& right);
We can overload assignment operator as a normal function, but we cannot overload assignment operator as a friend function. Why?
Because the C++ Standard says so, Article 13.5.3/1:
An assignment operator shall be
implemented by a non-static member
function with exactly one parameter.
Because a copy assignment operator
operator= is implicitly declared for a
class if not declared by the user
(12.8), a base class assignment
operator is always hidden by the copy
assignment operator of the derived
class.
That's all you really need to know. A friend function is not a member function, so it cannot be used to overload the assignment operator.
If you would like to write:
MyClassObject = MyFriendObject;
Then you would want to implement a constructor that takes a const reference to the friend class as it's parameter.
The difference between overloading by friend function and overloading by member function is that the calling object must be the first operand in overloading by member function, while there is no restriction in overloading by friend function. This is the reason behind the standard. Similarly, some other operators requiring the first operand to be the calling function must be overloaded using member functions (examples: =, [], ->, and ( )).
You cannot "extend" the assignment operator with a "free function" outside the class, but you can design the class so it will allow it:
Data.h
class Data {
public:
Data& operator=(const Data& lhs) { /*...*/; return *this; }
template <typename T> Data& operator=(const T& lhs) {
return assign(*this, lhs); // Magic right here...
}
private:
// ...
};
Point.h
class Point {
public:
float x,y;
Point& operator=(const Point& lhs) { x = lhs.x, y = lhs.y; return *this; }
template <typename T> Point& operator=(const T& lhs) {
return assign(*this, lhs); // Magic right here...
}
};
Assignment.h
Data& assign(const Data& lhs, const Point& rhs) {
lhs["x"] = rhs.x;
lhs["y"] = rhs.y;
return lhs;
}
Point& assign(const Point& lhs, const Data& rhs) {
rhs.query("x", lhs.x) || rhs.query(0, lhs.x);
rhs.query("y", lhs.y) || rhs.query(1, lhs.y);
return lhs;
}
if i have for example class A which contains the functions:
//this is in A.h
friend const A operator+ (const A& a,const A& b);
friend const A operator* (const A& a,const A& b);
which is a global (for my understanding). this function implemented in A.cpp.
now, i have class B which also contains the functions, and the member:
//this is in B.h
friend const B operator+ (const B& a,const B& b);
friend const B operator* (const B& a,const B& b);
A _a;
instead of using two seperate methods, i want to create single method in B.h:
static const B Calc(const B&, const B&, funcP);
which implemented in B.cpp and funcP is typedef to the pointer to the function above:
typedef const A (*funcP) ( const A& a, const A& b);
but when i tried to call Calc(..) inside the function i get this error:
"unresolved overloaded function type". i call it this way:
friend const B operator+ (const B& a,const B& b){
...
return B::Calc(a,b, &operator+);
}
what am i doing wrong?
Overloaded functions are usually resolved based on the types of their arguments. When you make a pointer to a function this isn't possible so you have to use the address-of operator in a context that is unambiguous.
A cast is one way to achieve this.
static_cast<funcP>(&operator+)
Don't do that. It's ugly, error-prone, and hard to understand and maintain.
This is what templates were invented for:
template< typename T >
static T Calc(const T& lhs, const T&, funcP rhs)
{
return lhs + rhs;
}
(Note that I removed the const from the function's return type. Top-level const on non-referencing types makes no sense.)
"which is a global (for my understanding)."
not for microsoft c at least
"A friend function defined in a class is not supposed to be treated as if it were defined and declared in the global namespace scope" from ms visual c++ help