Why am I not provided with a default copy construc

2019-01-17 10:58发布

问题:

This code:

class X {
  int member;  
};

volatile X a;
X b = a;

Fails with the error:

prog.cpp:6:7: error: no matching function for call to ‘X::X(volatile X&)’
prog.cpp:6:7: note: candidates are:
prog.cpp:1:7: note: X::X()
prog.cpp:1:7: note:   candidate expects 0 arguments, 1 provided
prog.cpp:1:7: note: X::X(const X&)
prog.cpp:1:7: note:   no known conversion for argument 1 from ‘volatile X’ to ‘const X&’

Is there any way I can get the compiler to generate a volatile copy constructor for me?

回答1:

The short answer is: Because the standard says you won't.

The C++ Standard 12.8/9 (Draft N3242) tells:

The implicitly-declared copy constructor for a class X will have the form

  • X::X(const X&)

if

  • each direct or virtual base class B of X has a copy constructor whose first parameter is of type const B& or const volatile B&, and
  • for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy constructor whose first parameter is of type const M& or const volatile M&. [Note: 119]

Otherwise, the implicitly-declared copy constructor will have the form

  • X::X(X&)

Note 119 says:

This implies that the reference parameter of the implicitly-declared copy constructor cannot bind to a volatile lvalue; see C.1.9.

In C.1.9 you'll find:

The implicitly-declared copy constructor and implicitly-declared copy assignment operator cannot make a copy of a volatile lvalue. For example, the following is valid in ISO C:

struct X { int i; };
volatile struct X x1 = {0};
struct X x2(x1); // invalid C++
struct X x3;
x3 = x1; // also invalid C++

Rationale: Several alternatives were debated at length. Changing the parameter to volatile const X& would greatly complicate the generation of efficient code for class objects. Discussion of providing two alternative signatures for these implicitly-defined operations raised unanswered concerns about creating ambiguities and complicating the rules that specify the formation of these operators according to the bases and members.



回答2:

The key problem is that you provide no assignment constructor. Consequently, the compiler generate a default one for you:

X& X::operator =(const X& x){
   this.member = x.member;
   return *this;
}

The default assignment constructor accepts argument type as const X& in which the const is a low-level const and won't be ignored as top-level const.

Your code X b = a means to call the default constructor. But your argument a has type volatile X (can be converted to volatile X & and volatile const X &) cannot be converted to const X& implicitly.

So you should define your own assignment constructor as

X& X::operator =(volatile const X&);

EDIT
It shocks me that so many guys think the copy constructor (or call copy initialization) is called when using assignment operator. Maybe call it assignment operator is not common. However, what I care is which method is called.

You can refer to this post:Copy constructors, assignment operators, and exception safe assignment and Default assignment operator


EDIT
I made a mistake previously. X b = a is just a initialization process. No assignment is involved. Apologize for my error message.