27

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?

Eric
  • 95,302
  • 53
  • 242
  • 374
  • You need to declare `b`as `volatile`. – David G Jun 20 '13 at 15:12
  • 1
    But I want a non-volatile copy! – Eric Jun 20 '13 at 15:13
  • 3
    `volatile X&` cannot be converted to `const X&` because the two qualifiers contradict each other: `const` says "read it once, it's not going to change", while `volatile` says "read it every time, because it can change". There must be some smart rule in the C++ standard that prohibits making this conversion implicitly. – Sergey Kalinichenko Jun 20 '13 at 15:18
  • 6
    Surely const says "I won't change it", and volatile says someone else might. – doctorlove Jun 20 '13 at 15:22
  • 3
    @dasblinkenlight: So what does `const volatile &X` mean then? – Eric Jun 20 '13 at 15:22
  • 1
    It is strange that copying from volatile types is allowed by default for integral types, but not even casting explicitly works for UDTs. – Wug Jun 20 '13 at 15:24
  • 1
    @Eric: It means you are not going to write that value, still the compiler should not optimize away apparently redundant reads because the value may change between two reads (even if `x` is not a shared variable) – Andy Prowl Jun 20 '13 at 15:29
  • 5
    I also need to disagree with "`const` says 'read it once, it's not going to change'". A `const X&` reference does NOT allow that sort of optimization in any context where it wouldn't also be valid for a plain `X&` reference. – aschepler Jun 20 '13 at 16:43
  • 3
    @dasblinkenlight: There is no contradiction. `const` merely prevents modification, and `volatile` merely means reads and writes are observable. `const volatile` is a read-only variable, where reading the variable is observable. – GManNickG Jun 20 '13 at 16:51

2 Answers2

17

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.

Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • "In C.1.9 you'll find" - That doesn't make much sense to me. C doesn't have copy constructors. Never mind, I've just noticed that you're referring to the Annex C of the C++ standard. – Alexander Nov 25 '20 at 12:31
-3

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.

Zachary
  • 1,633
  • 2
  • 22
  • 34
  • 4
    Nope. A constructor like `X (volatile const X &x) : member(x.member) { }` will be required since `X b = a;` is copy-initialization. There is no such thing like "assignment constructor". There is either a constructor or an assignment operator. – Pixelchemist Jun 20 '13 at 16:25
  • @Pixelchemist What you provide is called "copy constructor". These two are different things. It is called **rule of three** in C++. – Zachary Jun 20 '13 at 16:31
  • The rule of three mainly applies to classes managing resources. In this case a non-explicit copy constructor is required to do the conversion from volatile X to X. – Pixelchemist Jun 20 '13 at 16:34
  • @Pixelchemist Yeah. Rule of three mainly applies to classes with resources. The author use assignment operator (i.e. =) to construct a new object. Assignment constructor should be called. – Zachary Jun 20 '13 at 16:42
  • There is no _"assignment constructor"_. [Read this.](http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-direct-initializati) – Pixelchemist Jun 20 '13 at 16:44
  • @Pixelchemist Yes. From [Here](http://www.cplusplus.com/articles/y8hv0pDG/) and [Here](http://www.cplusplus.com/forum/beginner/55098/), we see it is called "assignment operator" which is called when using "=" to assignment one instance to another. – Zachary Jun 20 '13 at 16:55
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/32092/discussion-between-zack-and-pixelchemist) – Zachary Jun 20 '13 at 17:05
  • 1
    Please [edit] your answer to take into account that the proper solution is to define a copy constructor taking a volatile. This would be better than writing something false and then add errata to it. – YSC Mar 21 '18 at 17:12