C++ - What does reference& operator= do here - c++

I came across this piece of code while reading some stuff about vectors in C++11 STL. It uses an assignment of operator=. Now i can't figure out exactly what it does.
Here is the code :
namespace std {
template <typename Allocator> class vector<bool, Allocator> {
public:
// auxiliary proxy type for element modifications:
class reference {
...
public:
reference& operator=(const bool ) noexcept; // assignments
reference& operator=(const reference&) noexcept;
operator bool( ) const noexcept; // automatic type conversion to bool
void flip( ) noexcept;
// bit complement
};
...
// operations for element access return reference proxy instead of bool:
reference operator[]( size_type idx );
reference at( size_type idx );
reference front( );
reference back( );
};
}
From the above code i can understand that it returns a type of class reference. But what i can't understand is this statement reference& operator=(const reference&) noexcept; .
Please let me know what is the actual meaning of this statement in this context

The reference class is what's known as a proxy, it masquerades as another type to give it behaviors you couldn't get otherwise. In this case it's substituting for bool, thus it needs to be assignable to bool. You can see that there are two operator= methods on this class, one that takes bool and one that takes reference&.
One of the rules for operator= is that it must return a reference to the object, so that you can chain the = operators:
b1 = b2 = false;

Use of reference as a class is a poor choice. But...
The function
reference& operator=(const bool ) noexcept;
returns a reference to an object of type reference. Most likely, the return statement in the function looks like:
return *this;
Take a simple class:
struct A
{
A(int in) : data(in) {}
A& operator=(int in)
{
data = in;
return *this;
}
A& operator=(A const& rhs)
{
data = rhs.data;
return *this;
}
};
Usage:
A a(10); // a.data is 10
a = 20; // a.data is 20
A b(5); // b.data is 5
a = b = 35; // a.data as well as b.data are 35

You're right that it returns a reference object (Actually it returns a reference to a reference object - that's what the & means).
The method name indicates that you're overloading the assignment operator =.
The parameter to the method means that it takes an immutable (the const keyword) reference to a reference object.
The noexcept keyword promises that the method will not throw an exception. If you break that promise, it your program will throw a runtime exception.
C++ programmers would interpret this as a copy assignment operator.

Related

operator+ overload error. Unable to return reference to object

There is the class:
class MyClass
{
private:
double value;
public:
MyClass()
{
this->value = 0;
}
MyClass(double value)
{
this->value = value;
}
MyClass& operator + (MyClass & outerObj);
};
Why does that overload work:
MyClass& MyClass::operator + (MyClass & outerObj)
{
MyClass retObject(this->value + outerObj.value);
return retObject;
}
But does this one not?
MyClass& MyClass::operator + (MyClass & outerObj)
{
return MyClass(this->value + outerObj.value);
}
VC++ issues a warning:
MyClass::MyClass(double value)
+3 overloads
initial value of reference to non-const must be an lvalue
I can not understand what the problem is. After all, it returns essentially the same thing.
The problem of the 1st code snippet, you're trying to return a reference being bound to local object. The local variable will be destroyed when get out of the function, then the returned reference is always dangled.
The problem of the 2nd code snippet, you're trying to bind a temporary object to lvalue-reference to non-const, which is ill-formed.
You should change the operator+ from return-by-reference to return-by-value, which will make both returning local object or temporary object work fine, and keep you away from the above troubles. e.g.
MyClass MyClass::operator + (const MyClass & outerObj) const {
...
}

Avoid indirect instantiation from private constructor through operation

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.

Return type of operator= - reference or value?

What is the difference between returning from function "operator="
by reference
by value
? Both version seems to yield a correct result in the example below.
#include <iostream>
using namespace std;
class CComplexNumber{
float m_realPart;
float m_imagPart;
public:
CComplexNumber(float r,float i):m_realPart(r),m_imagPart(i){}
//the following can be also
//CComplexNumber& operator=(const CComplexNumber& orig){
CComplexNumber operator=(const CComplexNumber& orig){
if (this!=&orig){
this->m_realPart=orig.m_realPart;
this->m_imagPart=orig.m_imagPart;
}
return *this;
}
friend ostream& operator<<(ostream& lhs,CComplexNumber rhs){
lhs<<"["<<rhs.m_realPart<<","<<rhs.m_imagPart<<"]"<<endl;
}
};
int main() {
CComplexNumber a(1,2);
CComplexNumber b(3,4);
CComplexNumber c(5,6);
a=b=c;
cout<<a<<b<<c;
return 0;
}
Returning by value returns a copy of the object. Returning by reference returns the object itself.
Which one you want to use depends on how you want to use the value that was returned. If you want to modify it without affecting the original object (after returning), return by value; otherwise return by reference.
The convention when using the operator= member function is to return by reference. This allows you to chain operations on the object:
CComplexNumber a(1,2);
CComplexNumber b(3,4);
(a = b) *= 2; //Assignment of b to a, then scale by 2
Now, with return by value after the assignment, the *= would not modify the value a, since a copy of a would be scaled by 2. With return by reference, b would be assigned to a and a itself would be scaled by 2.
Returning a reference (mutable) is the least surprising, and what you really should choose for an operation which is so common it is implicitly declared. Reference is the return type of the default/implicit definitions.
Example:
A operator=(const A&) = default; // ERROR
const A& operator=(const A&) = default; // ERROR
A& operator=(const A&) = default; // OK
Some compilers will kindly warn you if you do not return a reference in a user defined function.
In addition to the surprising (and sometimes costly) copy, it also avoids slicing.
Returning a const reference could forbid a move.

const overloaded operator[] function and its invocation

I define two versions of overloaded operator[] function in a class array. ptr is a pointer to first element of the array object.
int& array::operator[] (int sub) {
return ptr[sub];
}
and
int array::operator[] (int sub) const {
return ptr[sub];
}
Now, if I define a const object integer1 the second function can only be called..... but if I make a non-const object and then invoke as below:
cout << "3rd value is" << integer1[2];
which function is called here?
In your second example, the non-const version will be called, because no conversion is required, and a call that requires no conversion is a better match than one that requires a conversion.
Ultimately, however, you have a basic problem here: what you really want is behavior that changes depending on whether you're using your object as an rvalue or an lvalue, and const doesn't really do that. To make it work correctly, you normally want to return a proxy object, and overload operator= and operator T for the proxy object:
template <class T>
class myarray {
T *ptr;
class proxy {
T &val;
proxy &operator=(proxy const &p); // assignment not allowed.
public:
proxy(T &t) : val(t) {}
operator T() const { return val; }
proxy &operator=(T const&t) { val = t; return *this; }
};
proxy const operator[](int sub) const { return proxy(ptr[sub]); }
proxy operator[](int sub) { return proxy(ptr[sub]); }
// obviously other stuff like ctors needed.
};
Now we get sane behavior -- when/if our array<int> (or whatever type) is const, our operator[] const will be used, and it'll give a const proxy. Since its assignment operators are not const, attempting to use them will fail (won't compile).
OTOH, if the original array<int> was not const, we'll get a non-const proxy, in which case we can use both operator T and operator=, and be able to both read and write the value in the array<int>.
Your const version should return const int& not int, so that the semantics are just the same between the two functions.
Once you've done that, it doesn't matter which one is used. If the const version has to be used because your object has a const context, then it will be... and it won't matter as you're not trying to modify anything. Otherwise, it'll use the non-const version... but with just the same effect.

What is "Extending move semantics to *this" all about?

Please, could someone explain in plain English what is "Extending move semantics to *this"? I am referring to this proposal. All what am looking for is what is that & why do we need that. Note that I do understand what an rvalue reference is in general, upon which move semantics is built. I am not able to grasp what such an extension adds to rvalue references!
The ref-qualifier feature (indicating the type of *this) would allow you to distinguish whether a member function can be called on rvalues or lvalues (or both), and to overload functions based on that. The first version gives some rationale in the informal part:
Prevent surprises:
struct S {
S* operator &() &; // Selected for lvalues only
S& operator=(S const&) &; // Selected for lvalues only
};
int main() {
S* p = &S(); // Error!
S() = S(); // Error!
}
Enable move semantics:
class X {
std::vector<char> data_;
public:
// ...
std::vector<char> const & data() const & { return data_; }
std::vector<char> && data() && { return data_; } //should probably be std::move(data_)
};
X f();
// ...
X x;
std::vector<char> a = x.data(); // copy
std::vector<char> b = f().data(); // move
For example, you can overload operators as free functions with rvalue references if you wish:
Foo operator+(Foo&& a, const Foo& b)
{
a += b;
return std::move(a);
}
To achieve the same effect with a member function, you need the quoted proposal:
Foo Foo::operator+(const Foo& b) && // note the double ampersand
{
*this += b;
return *this;
}
The double ampersand says "this member function can only be called on rvalues".
Whether or not you must explicitly move from *this in such a member function is discussed here.