Why Do Reference Type Members Cause Implicitly-Declared Copy Assignment Operator to Be Deleted

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



Leave a reply



Submit