The short answer:
Yes you can but the compiler won't do the job for you.
You cannot have an compiler-provided conversion from volatile T to T but a user-defined implicit conversion using a volatile-qualified constructor.
It is also impossible to declare such a conversion by using explicitly-defaulted versions of the special member functions (see long answer for reference).
You'll have to provide a user-defined way of conversion to enable such assignments. You can either
- use a non-explicit copy constructor with a cv-qualified argument for implicit user-defined conversion or
- a copy assignment operator taking a v-qualified argument.
Example:
X (X const volatile & xo);
X& operator= (X const volatile & xo);
The long answer with standard quotes 'n stuff or
why doesn't the compiler do this for me?
Way 1: User-provided constructor from volatile T
Standard, ISO 14882:2011, 4/3
An expression e
can be implicitly converted to a type T if and only if the declaration T t=e;
is well-formed, for some invented temporary variable t (8.5).
Since the declaration T t = e;
, where in this case e
is typed volatile T
, requires such a copy-initialization to be valid, you'll need a copy constructor from a volatile T.
I already answered (Why am I not provided with a default copy constructor from a volatile?).
Therefore you'll need to provide a user-defined way of copy-initialization of T from volatile T.
X (X const volatile & xo);
Note:
- This is a declaration, you'll also have to provide a definition.
- The constructor must not be explicit.
- Providing an user-defined copy constructor taking a volatile argument will result in the abscence of an implicitly generated default assignment operator.
This will make your assignment work.
Way 2: User-provided copy assignment operator from volatile T
Another way to make the assignment of your example code work is a copy assignment operator.
Unfortunatelly, the standard also does say that a compiler will not provide implicit copy assignment operators for the conversion of volatile to non volatile objects.
Standard, ISO 14882:2011, 12.8/18
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor. The implicitly declared copy assignment operator for a class X will have the form
X& X::operator=(const X&)
if
- each direct base class B of X has a copy assignment operator whose parameter is of type const B&, const volatile B& or 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 assignment operator whose parameter is of type const M&, const volatile M& or M. 122
Otherwise, the implicitly-declared copy assignment operator will have the form
X& X::operator=(X&)
Note 122 on 12.8/18
the reference parameter of the implicitly-declared copy assignment operator cannot bind to a volatile lvalue; see C.1.9.
In the other answer I quoted C.1.9 where it says:
The implicitly-declared copy constructor and implicitly-declared copy assignment operator cannot make a copy of a volatile lvalue. [ ... ]
The result is that we'll have to provide a suitable copy assignment operator if we want to have one.
X& operator= (X const volatile & xo);
Also note that you cannot declare an assignment/constructor from volatile explicitly-defaulted.
C++11 Standard 8.4.2/1
A function that is explicitly defaulted shall
- be a special member function,
- have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared, and
- not have default arguments.
The following Note was removed from the final C++11 standard but was present in the Draft N3242. It still holds.
[ Note: This implies that parameter types, return type, and cv-qualifiers must match the hypothetical implicit declaration. —end note ]
Since the hypothetical implicit declaration is non-volatile you cannot have the defaulted.