Why do reference type members cause implicitly-declared copy assignment operator to be deleted
References are bound to an object when they are initialized and can never be altered after that, everything else you do to them affects the object they are bound to, not the reference itself.
So a reference member is set during construction, and never altered. Since the purpose of an assignment operator is to alter members after construction, it doesn't make sense to generate an implicit assignment operator when one of the member can never be altered. The compiler refuses to try and guess what you want it to do and forces you to provide your own assignment operator with the semantics you want.
Do I need to do anything in my operator= such as explicitly declare return *this or will the compiler (g++) handle this for me?
You absolutely definitely 100% need to return *this;
The only time you don't need an explicit return in C++ is if your function returns void
or in main()
(where there is an implicit return 0;
if you reach the end of the function) or in unusual cases such as functions that never return (either looping forever or throwing an exception).
Do I have to do anything special with the reference member?
It depends what semantics you expect assignment of your type to have.
If you don't want it to change the object the reference is bound to, fine, do nothing with it.
If you want assignment to alter the object the reference is bound to, you need to do that.
If you want the reference to be re-bound to a different object, you're out of luck, C++ doesn't allow that.
Why are copy operators implicitly deleted in classes with reference fields?
2) Why does it matter if B has a copy constructor or operator if I'm trying to copy the reference, not the object? Am I not doing that part right?
Copy assignment of a reference assigns the referred object. It is not possible to re-seat a reference i.e. it is not possible to make reference refer another object.
1) Why is the implicit copy assignment operator deleted in B? Or even A for that matter?
Because they both have a reference member.
Why are copy operators implicitly deleted in classes with reference fields?
Because the assignment of the referred object, is counter-intuitive default behaviour for an assignment operator of a class, and therefore such assignment operator is not generated implicitly.
Deleted implicitly-declared copy assignment operator
It seems that clang is right,
Although not yet confirmed, there is a report on the subject for gcc
and as it was pointed out, the two rules relevant to this case don't apply
[class.copy.assign]/7
(7.2) a non-static data member of const non-class type (or array thereof), or
[...]
(7.4) a direct non-static data member of class type M (or array thereof) or a direct base class M that cannot be copied/moved because overload resolution ([over.match]), as applied to find M's corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator.
Why synthesized copy-assignment operator is defined as deleted if the class has a reference member?
A reference can't be reassigned once it's created. That means it's impossible to make a proper assignment operator if the class contains a reference member.
A copy constructor is a different matter, because the reference can be assigned at object creation.
Why GCC refuses a const reference within a copy-assignment operation?
or we can NOT delete it?
You just don't need to do that. If you provide a user-defined copy assignment operator, then no other ones will be implicitly-declared, i.e. only the user-defined one will exist.
If you do that, the copy assignment operator you explicitly marked as delete
will participate in overload resolution; when it's selected the compilation fails. For ob = oa;
, the operator=( ClassA & )
is a better match, if it doesn't exist, operator=( const ClassA & )
will be used and work fine.
So in this case you can just do
class ClassA {
public:
ClassA & operator=( ClassA && ) {
cout << "ClassA & operator=( ClassA && ) executed." << endl;
return *this;
}
ClassA & operator=( const ClassA & ) {
cout << "ClassA & operator=( const ClassA & ) executed." << endl;
return *this;
}
};
Deleted assignment operator error which isn't used
Consider how the erase
function might be implemented. It might look something like this:
constexpr iterator erase(iterator pos) {
for (auto i = pos; i + 1 != end(); i++) {
*i = move(*(i + 1));
}
pop_back();
return pos;
}
(The real parameter type is const_iterator
, but let's ignore this detail.)
If you choose to erase the last element of the vector, then the loop will run zero times and the assignment operator will be invoked zero times. However, you still force the compiler to instantiate the entire body of the erase
function. When this happens, the compiler must perform overload resolution and check whether there is a usable assignment operator. If there is not, a compilation error occurs.
Expecting your program to compile is like expecting this program to compile:
void foo() = delete;
void bar(int num_times) {
while (num_times--) foo();
}
int main() {
bar(0);
}
Would this program compile? Of course not. Even though foo
will never be invoked, the mere fact that you attempt to compile some code that contains a call to foo
will make the program ill-formed.
With a std::vector
, it is possible for you to say "I promise that I will only be removing the last element, so please do not attempt to compile any code that requires the assignment operator". To do this, use the pop_back
function instead of erase
.
Why are copy operations deleted when move operations are declared?
When a class would be moved but for the fact that no move constructor is declared, the compiler falls back to copy constructor. In the same situation, if move constructor is declared as deleted, the program would be ill-formed. Thus, if move constructor were implicitly declared as deleted, a lot of reasonable code involving existing pre-C++11 classes would fail to compile. Things like myVector.push_back(MyClass())
This explains why move constructor cannot be implicitly declared deleted when copy constructor is defined. This leaves the question of why copy constructor is implicitly declared deleted when move constructor is defined.
I don't know the exact motivation of the committee, but I have a guess. If adding a move constructor to existing C++03-style class were to remove a (previously implicitly defined) copy constructor, then existing code using this class may change meaning in subtle ways, due to overload resolution picking unexpected overloads that used to be rejected as worse matches.
Consider:
struct C {
C(int) {}
operator int() { return 42; }
};
C a(1);
C b(a); // (1)
This is a legacy C++03 class. (1) invokes an (implicitly defined) copy constructor. C b((int)a);
is also viable, but is a worse match.
Imagine that, for whatever reason, I decide to add an explicit move constructor to this class. If the presence of move constructor were to suppress the implicit declaration of copy constructor, then a seemingly unrelated piece of code at (1) would still compile, but silently change its meaning: it would now invoke operator int()
and C(int)
. That would be bad.
On the other hand, if copy constructor is implicitly declared as deleted, then (1) would fail to compile, alerting me to the problem. I would examine the situation and decide whether I still want a default copy constructor; if so, I would add C(const C&)=default;
Related Topics
How to Create a Template Function Within a Class? (C++)
Difference Between Cc, Gcc and G++
How to Implement Coroutines in C++
Which One Will Execute Faster, If (Flag==0) or If (0==Flag)
Is There Any Reason to Use C Instead of C++ for Embedded Development
How to Pass Arguments and Redirect Stdin from a File to Program Run in Gdb
How to Use the Priority Queue Stl for Objects
How to Get the Maximum or Minimum Value in a Vector
Where Would You Use a Friend Function VS. a Static Member Function
Hooking Directx Endscene from an Injected Dll
Default Parameters with C++ Constructors
Complete Example Using Boost::Signals for C++ Eventing
C++ Constructor/Destructor Inheritance