I'm given to understand, thanks to the VC++ compiler, that you cannot overload functions with only a differing return type.
class MyClass {
public:
MyClass MyClass::operator+(MyClass other) {
return MyClass(n + other.getn());
}
MyClass() = default;
MyClass(int my_n) : n{ my_n } {}
int getn() {
return n;
}
private:
int n{ 0 };
};
int main(){
MyClass m1(7), m2(5);
MyClass m3 = m1 + m2;
return 0;
}
However, what if I would like to return an integer, say 12, when I add them, I cannot simply add them together and overload the operator+ again because it doesn't allow overloading where the only difference is the return type. I'm very new to C++. The only solution I could come up with is:
int i = (m1+m2).getn();
But that seems wasteful to create an instance when you will never use it again.
I think what you want is to be able to write int i = m1 + m2;, which means you should create a conversion operator.
class MyClass
{
operator int()
{
return n;
}
};
int i = m1 + m2; //creates temporary, calls operator int, saves value in i
It will still create a temporary, but that's how operator+ works. You could always write int i = m1.getn() + m2.getn();, too.
Overloading doesn't consider about return type.
From the standard, $13.3/1 Overload resolution [over.match]
The selection criteria for the best function are the number of arguments, how well the arguments
match the parameter-type-list of the candidate function, how well (for non-static member functions) the object matches the implicit object parameter, and certain other properties of the candidate function.
and
128) ... candidate call functions that cannot be differentiated one from the other by overload
resolution because they have identical declarations or differ only in their return type.
You might want to provide two operator+ for MyClass, they only differ at the return type of MyClass and int, and use them as
MyClass m1(7), m2(5);
MyClass m3 = m1 + m2;
int x = m1 + m2;
but the return value could be omitted, then which one should be invoked? It's ill-formed.
m1 + m2;
// or m1.operator+(m2);
You could provide another named member function for it, such as
class MyClass { public:
int add_return_int(MyClass other) {
return n + other.getn();
}
...
or as free function
int add_return_int(MyClass lhs, MyClass rhs) {
return lhs.getn() + rhs.getn();
}
Related
I am trying to create a class whose objects must contain a short description ("name") of what their value represent. Therefore the only public constructor should take a string as argument.
For the operations, however, I need to create temporary (no relevant name) object to calculate the value to be assigned to an already existing object. For that I have implemented a private constructor, which should not be used, neither directly nor indirectly, to instantiate a new object - these temporary objects should only be assigned to an already existing object, through operator=, which only copies the value rather than name and value.
The problem comes with the use of "auto". If a new variable is declared as follows:
auto newObj = obj + obj;
the compiler deduces the return type of operator+ and directly assign its result to newObj. This results in an object with a irrelevant name, which should not be possible to instantiate.
Also, deducing the type of an already existing object should still be possible from some functions, like:
auto newObj = obj.makeNewObjWithSameTypeButOtherName("Other name");
Follows a code demonstrating the problem:
#include <iostream>
#include <string>
using namespace std;
template<class T>
class Sample
{
public:
Sample(const string&);
Sample<T> makeNewObj(const string&);
// Invalid constructors
Sample();
Sample(const Sample&);
void operator=(const Sample&);
void operator=(const T&);
Sample<T> operator+(const Sample&) const;
void show(void);
private:
// Private constructor used during operations
Sample(const T&);
T _value;
string _name;
};
template<class T>
Sample<T>::Sample(const string& name)
{
this->_name = name;
this->_value = 0;
}
template<class T>
Sample<T>::Sample(const T&value)
{
this->_name = "Temporary variable";
this->_value = value;
}
template<class T>
Sample<T>
Sample<T>::makeNewObj(const string& name)
{
return Sample<T>(name);
}
template<class T>
void
Sample<T>::operator=(const Sample& si)
{
this->_name = this->_name; // Make explicit: Never change the name
this->_value = si._value;
}
template<class T>
void
Sample<T>::operator=(const T& value)
{
this->_name = this->_name; // Make explicit: Never change the name
this->_value = value;
}
template<class T>
Sample<T>
Sample<T>::operator+(const Sample& si) const
{
// if any of the two values are invalid, throw some error
return Sample<T>( this->_value + si._value );
}
template<class T>
void
Sample<T>::show(void)
{
cout << _name << " = " << _value << endl;
}
int main()
{
Sample<double> a("a"), b("b");
a = 1; // Sample::operator=(const T&)
b = 2.2; // Sample::operator=(const T&)
a.show(); // Output: a = 1
b.show(); // Output: b = 2.2
auto c = a.makeNewObj("c"); // Should be possible
c = a + b; // Sample::operator+(const Sample&) and Sample::operator=(const Sample&)
c.show(); // Output: c = 3.2
// Sample<double> d; // Compiler error as expected: undefined reference to `Sample::Sample()'
// auto f = a; // Compiler error as expected: undefined reference to `Sample::Sample(Sample const&)'
// This is what I want to avoid - should result in compiler error
auto g = a+c; // No compiler error: uses the private constructor Sample::Sample(const T&)
g.show(); // Output: Temporary variable = 4.2 <-- !! Object with irrelevant name
}
A quick workaround is to not return a temporary Sample<T> from operator +. Since you only want the value part you can just return that instead. That changes the code to
T operator+(const Sample&) const;
template<class T>
T
Sample<T>::operator+(const Sample& si) const
{
// if any of the two values are invalid, throw some error
return this->_value + si._value;
}
and then
auto g = a+c;
will make g whatever T is and g.show(); will not compile as g isn't a Sample<T>.
Sample<double> g = a+c;
Will also not work as it tries to construct g from a value and that constructor is private.
This will require adding
friend T operator+(T val, Sample<T> rhs) { return val + rhs._value; }
If you want to be able to chain additions like
a + a + a;
Somewhat related but also orthogonal to NathanOliver's answer:
You are mixing different concepts here. You have the notion of, essentially, NamedValue with Sample, but you are trying to make each expression formed out of arithmetics on NamedValue also a NamedValue. That is not going to work - the expression (by your semantics) does not have a name, so it should not be a NamedValue. Therefore, having NamedValue operator+(const NamedValue& other) is not meaningful.
Nathan's answer resolves this by making additions return T instead. That's pretty straightforward.
However, note that since a + b must have a type, you cannot stop auto g = a + b from compiling, even if it is demonstrably incorrect code. Ask Eigen, or any other expression template library. This remains true no matter how you choose the return type of operator+. So this wish of yours cannot be granted, unfortunately.
Still, I would suggest that you don't use plain T as return type but rather another class, say, Unnamed<T>:
template<class T>
class Unnamed
{
public:
explicit Unnamed(const T& value) : _value(value) {};
Unnamed<T> operator+(const Unnamed<T>& rhs) const
{
return Unnamed<T>(_value + rhs._value);
}
friend Unnamed operator+(const Unnamed& lhs, const Sample<T>& rhs);
friend Unnamed operator+(const Sample<T>& lhs, const Unnamed& rhs);
private:
T _value;
};
This allows you to do your checks and what have you on every operation (because the middle + in (a + b) + (c + d) cannot accept NamedValues, see above) instead of only when converting back to a named value.
Demo here.
You can increase the compile-time safety slightly by only allowing construction of Sample from Unnamed temporaries: https://godbolt.org/g/Lpz1m5
This could all be done more elegantly than sketched here. Note that this is moving exactly in the direction of expression templates though.
My suggestion would be to change the signature of the + operator (or any other operation needs implemented) to return a different type.
Than add an assignment operator accepting this "different type", but do not add a copy constructor - alternatively, for better error reporting, add a deleted one.
This would require more coding, since you would probably want to define "operations" on this type as well, so that chaining works.
One operation that continues to confuse me in C++ is how operator= interacts with objects.
I'm not sure what exactly is occurring behind the scenes when executing a program such as this:
class ObjectType {
private:
int variable;
public:
ObjectType(int v) {
variable = v;
}
};
int main() {
ObjectType object1(10);
ObjectType object2(15);
object1 = object2;
return 0;
}
From my understanding, it makes all of the member variables in the first object equal to the corresponding member variables in the second object. In this case, the "variable" of object1 would now equal 15, but I'm not sure.
Any elaborations would be greatly appreciated, thank you.
When you type object1 = object2; Here it invokes = operator overloaded function, if operator overloading function for assignment operator is not
defined , then compiler does it for us by-default ,the compiler just does a member-wise copy from one object to another object.
Here is basic code :
class ObjectType {
private:
int variable;
public:
ObjectType(int v) {
variable = v;
}
void operator () (int n1){ /** () operator overloaded function if needed **/
variable = n1;
}
};
int main() {
ObjectType object1(10);//parameterized constructor will be called
ObjectType object2(15);
/** use case when () operator overloaded function will be called
ObjecType object1;
object1(10);// invokes () overloaded function
**/
object1 = object2;/** if = operator overloaded function is not defined then consider default one provided by compiler */
return 0;
}
I'm new to C++ and I'm trying to understand how Class works. I made a simple example for myself when I encounter this problem. My class has one private property num and I'm trying to initialize it with an int this way: Number one = Number::ONE; but it doesn't work. However, when I do this it works fine: Number one; one = Number::ONE. I prefer the first option. Please help!
Here is my complete code:
class Number {
public:
enum {ONE, TWO, THREE, FOUR};
Number();
void print() const;
Number& operator=(int);
private:
int num;
};
Number& Number::operator=(int n) {
num = n;
return *this;
}
int main(int argc, const char * argv[]) {
Number n = Number::ONE; // doesn't work :(
n.print();
return 0;
}
Number::Number() {
num = 0;
}
void Number::print() const {
cout << num << endl;
}
In C++, when you write
Number one = Number::ONE;
the compiler will not use the assignment operator to initialize one. The operator= function is only invoked when you have an existing object that you want to reassign a new value. Instead, in this case, the compiler tries to invoke a conversion constructor, a constructor that takes in an object of the type on the right-hand side of the equality. Since you haven't defined a constructor like that, you're getting a compiler error.
One way to do this would be something like this:
class Number {
public:
Number(int value); // <-- Conversion constructor
...
};
Number::Number(int value) {
num = value;
}
Now, the code you've given here will compile properly.
You may want to do some reading into copy constructors, assignment operators, conversion constructors, and conversion assignment operators, since they're one of the trickier parts of routine C++ and often trip up people transitioning from basic C++ to more intermediate-level language techniques.
you didn't overload the constructor to take an integer as parameter that is why you get the error:
class Number
{
public:
enum {ONE, TWO, THREE, FOUR};
Number(int x) : num(x){}
void print() const;
Number& operator=(int);
private:
int num;
};
Let's say we have this class A:
class A
{
public:
int a;
A(int b)
{
a = b;
}
};
I would like to create a + overload such that I could use it like this
A a(1),b(2),c(3),&d;
d = a + b + c;
without modifying the content of each object. The next logical thing would be allocating a new chunk of memory each time like this:
A &operator+ (const A &b)
{
A *c = new A(a+b.a);
return *c;
}
But this would create a new problem: intermediate results are lost, causing memory leaks.
I could have easily solved this problem by making a static function that takes three A object references and stores the sum of the first two in the third, but I'm willing to bet that there must be some way to make + overload happen the way I want.
So the question is: is there any way I could use a chain of operator overloads that do not modify the operands without causing memory leaks?
You can simply use pass by value and do this:
A operator+ (A other) //pass by value
{
other.a += a;
return other;
}
Or, since the member a is publicly accessible, you can (rather should) make operator+ a non-member function as:
A operator+(A left, A const &right)
{
left.a += right.a;
return left;
}
Notice that the first argument is accepted by value, and second by reference. In this way, you don't need to declare a local variable in the function. You can use the first parameter; after all it is local to the function, you can do whatever you want to do with it: in this case, we just add right.a to it, and return it.
A better design of class would be this: (read the comments)
class A
{
int a; //make it private
public:
A(int b) : a(b) //use member initialization list
{
}
A& operator+=(A const & other) //add `+=` overload, as member of the class
{
a += other.a;
return *this;
}
};
//and make `+` non-member and non-friend
A operator+(A left, A const & right)
{
left += right; //compute this in terms of `+=` which is a member function
return left;
}
There is no need to use pointers inside operator+. You can allocate the intermediate object in the stack and then return it:
A operator+ (const A &b)
{
A c(a+b.a);
return c;
}
Or just:
A operator+ (const A &b)
{
return A(a+b.a);
}
Or even simpler:
A operator+ (const A &b)
{
return a+b.a;
}
Since this implicitly calls A::A(int).
Note that I removed the reference from the return type. You can't return a non-const reference to a local.
Then you would use it this way:
A a(1),b(2),c(3),d;
d = a + b + c;
Note that d is no longer a reference.
The title basically says it all. I mainly want to do this so that I can create an object (say, a custom string object) that can initialize the parameters of other functions in other APIs. Here's an example of me trying to get a custom integer class to work:
#include <iostream>
using namespace std;
class test
{
public:
int member;
test(int i) : member(i) {}
friend int &operator=(int &i, test t);
};
int &operator=(int &i, test t)
{
return (i = t.member);
}
int main()
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
Unfortunately I receive an error saying that operator= needs to be a member function. I understand the C++ standard's goal in preventing static and non-member overloads for the assignment operator from being implemented, but is there any other way to do this? Thanks for any help/suggestions!
This is not done with an assignment operator but with an overloaded typecast. This would make your main function work like expected:
#include <iostream>
using namespace std;
class test
{
public:
int member;
test(int i) : member(i) {}
operator int() const {return member;}
};
int main()
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
What you are trying to do needs an conversion operator
operator int()
{
return this->member;
}
For the class you are trying to write(containing only integer members), You do not need to overload the = operator.
= operator is one of the member functions that is generated by the compiler by default for every class. Caveat is, it does a simple bit by bit copy(shallow copy) of class members, since you have only integers it should be good enough for you.
You would need to overload the = operator if you had dynamically allocated pointers as member functions, because in that case a shallow copy of those pointers would result in all the objects containing a member pointer pointing to the same dynamic memory location & if one of the object finishes it lifetime, other objects are left with a dangling pointer.
As #Tony, aptly points in out comments Shallow copy is usually bad but not always. See his comments for a scenario.
If at all you want to overload the assignment operator check out the Copy and Swap Idiom to do it right way.
You should also check out the Rule of Three.
Try this:
class test
{
public:
int member;
test(int i) : member(i) {}
operator int() {return this->member;}
};
int main(void)
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
The assignment operator cannot be a friend function. The assignment operator can only be declared as a non-static member function. This is to ensure that it receives the L-value as its first operand. The same is true for the [], (), and -> operators. In your case, since int is an build-in type, you cannot use member function. You can implement operator int() to cast your user-defined type to int.