Why am I allowed to do this, why there is no ambiguity complain, and why is the class' method chosen with prior to the other one?
class EX
{
public:
//...
void operator+(const EX& ref)
{
cout << "A";
}
};
void operator+(const EX& ref1, const EX& ref2)
{
cout << "B" << endl;
}
int main()
{
EX obj1{20};
EX obj2{30};
cout << "obj1 + obj2 = " << obj1 + obj2 << endl;
return 0;
}
I was expecting the global function operator+ to be called an "B" printed on the screen, instead "A" was printed.
Your member overload accepts only non-const instances of EX for the first operand while the free overload accepts both const and non-const instances. By overload resolution rules, the non-const wins out because it exactly matches the type being passed in.
You'll get an ambiguity error if you make the member overload const, indicating that *this is const:
void operator+(const EX& ref) const
{
cout << "A";
}
Related
As we know, we normally return a copy to a new class when overloading an arithmetic operator. As an example, << operator is normally defined like this:
T1 operator<<(const T1& a, const T2& b) {
// apply b
return a;
}
But I wonder if it is generally valid to return a reference in this case or not. For example, is it possible that following code creates any invalid situation?
T1& operator<<(T1& a, const T2& b) {
// apply b
return a;
}
Why I need this? Let's assume I have a code like this:
class B {
public:
B() {}
~B() { std::cout << ss_.str() << "\n"; }
template<typename T>
B& operator<<(T val) {
ss_ << val;
return *this;
}
private:
std::stringstream ss_;
};
int main() {
B{} << "str" << "abc" << 1;
B{} << "str2" << "abc2" << 2;
}
In this code, constructor and destructor of type T1 will be called only once which will be really suitable here. But if I return by value for the << operator, constructor and destructor will be called for each temporary rvalues which is not great for my use case.
Is it possible that returning by reference create invalid code for overloading these operators?
Yes, it's not only valid, it's a common idiom when overloading << for stream outputs. For example:
std::ostream& operator<<(std::ostream& os, const MyData& data) {
os << data.a << ',' << data.b << ',' << data.c;
return os;
}
The rule about returning a reference is to never return a reference to a function local variable. This also includes function parameters that are passed by value.
It gets a little trickier when dealing with reference parameters.
const T1& operator<<(const T1& a, const T2& b) {
// apply b
return a;
}
is problematic since a could be bound to a temporary. That would mean that you are returning a reference to an object that is going to end at the end of the full expression the function call is in, which could cause trouble.
Using
T1 operator<<(T1& a, const T2& b) {
// apply b
return a;
}
You stop that from happening as now a can only bind to an lvalue so you don't have to worry about it living past the full expression the function is called in.
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;
To understand the working of objects in c++ better, I wrote this piece of code:
using namespace std;
char n[] = "\n";
class T
{
private:
int num;
public:
T ()
{
num = 0;
cout << n << (long)this % 0xFF << " created without param";
}
T (const int param)
{
num = param;
cout << n << (long)this % 0xFF << " created with param = " << param;
}
T (const T& obj)
{
num = obj.num;
cout << n << (long)this % 0xFF << " created as copy of " << (long)&obj % 0xFF;
}
const T& operator= (const T& obj)
{
if (this == &obj)
return *this;
num = obj.num;
cout << n << (long)this % 0xFF << " got assigned the data of " << (long)&obj % 0xFF;
return *this;
}
~T ()
{
cout << n << (long)this % 0xFF << " destroyed";
}
int get () const {return num;}
void set (const int param) {num = param;}
};
T PlusTen (T obj)
{
T newObj(5);
newObj.set( obj.get() +10 );
return newObj;
}
int main ()
{
T a, b(4);
a = b;
a = PlusTen(b);
cout << n;
return 0;
}
Its working fine, but when I remove the const qualifier in "return-type" and "parameter" of overloaded assignment operator like this below:
T& operator= (T& obj) // const removed
{
if (this == &obj)
return *this;
num = obj.num;
cout << n << (long)this % 0xFF << " got assigned the data of " << (long)&obj % 0xFF;
return *this;
}
Then this line of main function gives error:
a = PlusTen(b);
The error message being:
no match for 'operator=' (operand types are 'T' and 'T')
note:
candidate is: T& T::operator=(T&)
no known conversion for argument 1 from 'T' to 'T&'
If operand types of 'T' and 'T' are problematic, how come the line just above it (a = b;) is totally fine? They too are of operand types 'T' and 'T' !!
I found a relevant questoin here but no useful detail there:
why must you provide the keyword const in operator overloads
One person there says that if we don't use const in operator=, we can only use it for non-const objects. But in my case too both sides are non-const. Then why error? Especially when the line just above it, which is identical in operand types, compiles fine?
Compiler used: MinGW
PlusTen(b); is creating a temporary object. Since non-const references can not be bound to temporary objects, operator= can not be called here.
In a = b; b is not a temporary, it's a modifiable object (a so-called l-value). Non-const reference is successfully bound to it, and operator= is called.
For the extra fun, try defining your b as following:
const T b(4);
This function
T PlusTen (T obj)
{
T newObj(5);
newObj.set( obj.get() +10 );
return newObj;
}
returns a temporary object of type T. this temporary object can be bound with a constant reference.
This is important! This is the reason OP is confused!
Non-const references to temporary objects are not allowed in C++ !! In case of a = PlusTen(b);, as PlusTen(b) is a temporary value, the function operator= cannot bind the argument obj to PlusTen(b) value, because obj is non-const whereas PlusTen(b) can only be const.
So the compiler issues an error because the parameter of the assignment operator
T& operator= (T& obj)
^^^^^^
is not a constant reference.
The qualifier const in the return type makes no matter in the context how the operator is used in your program.
If I want to overload the << operator to use cout on a class, it should look like this:
template <typename coutT>
friend ostream& operator << (ostream &, const vector3D<coutT>&);
inside the class, and
template <typename coutT>
ostream& operator << (ostream & os,const vector3D<coutT>& v)
{
os << "x: " << v.x<< " y: " << v.y << " z: " << v.z;
return os;
}
on the outside. Please take note of the const at the second operand.
This sample of code works just fine. Now to the problem.
If I were to write the overload function using the getters for the fields, instead of addressing them directly (since operator<< is a friend), my compiler would throw an error:
template <typename coutT>
ostream& operator << (ostream & os,const vector3D<coutT>& v)
{
os << "x: " << v.getX() << " y: " << v.getY() << " z: " << v.getZ();
return os;
}
The error:
(VisualStudio2012) errorC2662: "this-pointer cannot be converted from "const vector3D" in "vector3D&""
An important note is that deleting the "const" at the second operand so that it's like
ostream& operator << (ostream & os,vector3D<coutT>& v){...}
ended compiler errors, but since I don't want to change v, it should really be a const.
I should also mention that I think it may have to do with method calls in general, but I'm not sure.
edit:
So it is solved, declaring functions as const sticks to const-correctness.
The error message explains it in the way that it cannot cast the const type to a non-const one.
btw.: I'm actually impressed about the quick responses.
The getter function should be declared const if you want to use it that way.
For example
int getValue() const {
return x;
}
Complete example:
#include <iostream>
#include <vector>
using namespace std;
class Foo {
int x;
public:
Foo(int a) : x(a) {
}
int getValue() const {
return x;
}
friend ostream & operator<<(ostream & out, const Foo & foo) {
return out << foo.getValue();
}
};
int main() {
vector<Foo> foo_vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (vector<Foo>::iterator it = foo_vec.begin(); it != foo_vec.end(); it++) {
cout << *it << ", ";
}
return 0;
}
Your problem is that you didn't mark the get functions const:
They need to look like this:
double getX() const;
You need to make the accessor functions const:
struct V
{
int getX() const { /* ... */ }
^^^^^
};
Only const member functions can be invoked on constant object values. In turn, const member functions cannot mutate the object. Thus const-correctness guarantees that a constant value cannot be mutated by invoking any of its member functions.
I would like to print the id of my object when the object is being evaluated (third line in my main function "one = two; should output "Object id: 2"). I know that the assignment operator is only invoked for the lvalue, a conversation operator to itself will never be called and I don't want to use the function call operator.
Any ideas how this can be done, which operator must I overload?
P.S.: Please ignore any saneness of the code, I only care about identifying the right to-be-overloaded operator.
#include <iostream>
class Object
{
public:
Object( int id ) : id_( id )
{
}
//Assignment operator only invoked for lvalue
Object& operator= (const Object& other)
{
std::cout << "Object id: " << id_ << std::endl;
return *this;
}
//Conversion operator to itself will never be called
operator Object() const
{
return *(this);
}
//Function call operator - not what I mean
Object operator()()
{
std::cout << "Object id: " << id_ << std::endl;
return *(this);
}
private:
int id_;
};
int main()
{
Object one(1);
Object two(2);
one = two;
one = two();
return 0;
}
The concept of "object evaluation" isn't really present in C++. An expression can be evaluated, of course, and operator overloading fits into this: foo = bar invokes any overloaded operator= which matches the operands (and in the case of operator= specifically, must be a member of the LHS class type). But it's the assignment that's being evaluated, not foo or bar (and note that in the context of the operator overload, these are present as pointers or references, so it's not inevitable that either one will be evaluated at all).
The appropriate solution here will depend on what it is you're trying to accomplish. But there's no one function that automatically fires whenever your code mentions bar.
#include <iostream>
class Object
{
public:
Object( int id ) : id_( id )
{
}
//Assignment operator only invoked for lvalue
Object& operator= (const Object& other)
{
std::cout << "Object id: " << other.id_ << std::endl;
return *this;
}
private:
int id_;
};
int main()
{
Object one(1);
Object two(2);
one = two;
return 0;
}
I tried this to see if Mat was right, turns out it compiles and prints "Object id: 2" as expected.
So, this line:
one = two;
Will call this function that you already have:
Object& operator= (const Object& other)
{
std::cout << "Object id: " << id_ << std::endl;
return *this;
}
However, let's note that there are two Object objects that are visible in that function.
Object& operator= (const Object& other)
{
std::cout << "Object(" << this->id_ << ")";
std::cout << " = ";
std::cout << "Object(" << other.id_ << ")";
std::cout << "\n";
return *this;
}
You can see a full example where we run this code at ideone.com.