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.
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.
I've defined a template class and overloaded operators. When compiling, I get the following error message:
error C2677: binary '+=' : no global operator found which takes type 'Class' (or there is no acceptable conversion)
Here is the relevant code of the class:
template<int foo>
class Class
{
private:
int value;
public:
template<int other_foo>
friend class Class;
// Constructors and copy constructors for several data type
// including other possibilities of "foo"; all tested.
Class& operator+=(const Class& x)
{
value += x.value;
return *this;
}
template<class T>
Class& operator+=(const T& x)
{
this += Class(x);
return *this;
}
};
If I create two objects of, e.g., Class<3>; the operator += works fine and does the right thing.
However, if I have an object of Class<3> and one of Class<2>, I get the above error which points at the line where "+=" is defined for T [the constructor for different value of foo works fine and is also tested].
What am I doing wrong? How can I resolve this error? The operator is defined, just a few lines above.
Assuming that the necessary constructor does indeed exist and work correctly, the error in the code you've posted is
this += Class(x);
which tries to modify the value of the immutable pointer this. It should be
*this += Class(x);
I think that there are two problems, both in this line:
this += Class(x);
One: the object added to should be *this instead of this, because the latter is a pointer, not the object itself.
Two: There is no conversion constructor from T to Class. That is, you cannot convert from Class<3> to Class<2> so the Class(x) will not compile. The solution would be to add it:
template<int other> Class(const Class<other> &o)
{}
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){
// ...
};
I was wondering what is the correct way of writing the operator + in C++ (assuming I am defining the addition operator for an object defined by a class c)?
c c::operator+(const c rhs) { /* do addition */ }
or
c c::operator+(const c& rhs) { /* do addition */ }
Let's make a concrete example:
class Int
{
int n_;
public:
// constructors...
Int & operator+=(Int const & rhs) { n_ += rhs.n_; return *this; }
Int & operator+=(Int && rhs) { n_ += rhs.n_; return *this; }
We've spelt out two overloads of the compound-addition which happen to be the same, but we can imagine that the rvalue reference overload provides some increased efficiency in general.
Now how to write operator+? One option is to take the RHS by value:
Int operator+(Int rhs) { return Int(*this) += std::move(rhs); }
This version is certainly easy to write and works. It's convenient because we only need a single overload to cover both lvalue and rvalue arguments. But note that this code requires copies to be made: The construction Int(*this) is not copy-elidable. So if you absolutely want to not impose any unnecessary cost on the user, you may wish to go the extra mile and reimplement the non-compound operator:
Int operator+(Int const & rhs)
{ Int result(*this); result += rhs; return result; }
Int operator+(Int && rhs)
{ Int result(*this); result += std::move(rhs); return result; }
There is a third option: If your operation is commutative, you can return the by-value argument:
Int operator+(Int rhs) & { return rhs += *this; }
Int operator+(Int rhs) && { return rhs += std::move(*this); }
The recommended way of defining operation + (and other similar operations like - etc.) is to define += operator and then define + in terms of +=. The operator += should be public member of the class, should accept one parameter by const reference, and should return reference to this:
c& operator+=(const c& other)
{
... // modify the object in appropriate way, e.g. if it has a member x do
... // x += other.x;
return *this;
}
Then it is easy to define operator+. It should be a global function defined outside the class:
c operator+(const c& lhs, const c& rhs)
{
c that = lhs;
that += rhs;
return that;
}
Of course one may define those operators in other ways, and often other ways may be better, but there should be some reason to do so. Otherwise it is best to stick with this default.
Generally, you should prefer the const reference version because you already guarantee that you will not alter the referenced object and it avoids calling the copy constructor. Other than that, there should be not much difference unless you do some unsafe stuff within the operator+() that would break in either case (like getting the address of the passed object or its members and making unsatisfied assumptions about object lifetime or having flawed copy constructor implementations).
Depending on the semantics of your class c, the copy constructor might also have some undesired side effects, but this is something only you as author of the c class can know of.
Further, depending on what you are doing in the operator+() and which code of class c is directly visible by the compiler, there are chances that the compiler will optimize the passing-by-value case so that your resulting machine code is the same.
The assignment operator can be declared as
T& operator= (const t&);
in a class, but the arithmetic operators cannot be defined that way. It has to be friend function. I don't understand why? Can you please explain ?
It is not mandatory that arithmetic operators should be friend
Well you can define like this:
MyClass MyClass::operator + (const MyClass& t) const
{
MyClass ret(*this);
ret += t;
return ret;
}
The a + b is really a syntax sugar, the compiler will expand it to a.operator+(b). The previous sample will work if all your objects are MyClass instances, but will not work if you have to operate with others types, ie 1 + a, will not work, this can be solved by using friends.
MyClass operator + (int i, const MyClass& t)
{
MyClass ret(i);
ret += t;
return ret;
}
This has to be done when the left hand side of the + operator is not a class, or it is a class but you can't add operator + to its definition.
I think that C++ FAQ Lite will give you a definitive answer.
They ideally should be globals and not necessarily friends, so that you can write:
yourtype v = 1;
yourtype w = 1 + v;
Since, 1 is not an object of yourtype, if the operator+ were a member it would throw a fit. However, making it global makes it convert the 1 to yourtype and then perform the operation. Making it a friend helps to extract and manipulate the members of yourtype as required -- though not required. As an example: You can implement the member function operator+= and use it in the implementation of the operator+.
The problem is that if you do something like this:
class A
{
A& operator+(int n);
// ...
}
int main()
{
A my_var;
int integer = 1;
A + integer; // compiles
integer + A // error: no function operator+(int, A) defined
}
it will not compile.
A solution is to define operator+(int, A) and operator+(A, int) as friends of A.
As a side note, the Boost Operators library makes this process very easy.