5

This code

struct T {
    int m_x;
    T(int x) : m_x(x) {}

    operator T() {
        return T(0);
    }
};

int main() {
    volatile T v(2);

    T nv(1);
    nv = v; // nv.m_x = 0
}

Gives:

prog.cpp: In function ‘int main()’:
prog.cpp:14:10: error: no match for ‘operator=’ in ‘nv = v’
prog.cpp:14:10: note: candidates are:
prog.cpp:1:8: note: T& T::operator=(const T&)
prog.cpp:1:8: note:   no known conversion for argument 1 from ‘volatile T’ to ‘const T&’
prog.cpp:1:8: note: T& T::operator=(T&&)
prog.cpp:1:8: note:   no known conversion for argument 1 from ‘volatile T’ to ‘T&&’

What typecast overload do I need to define for this to work?

Eric
  • 95,302
  • 53
  • 242
  • 374

3 Answers3

5

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.

Community
  • 1
  • 1
Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • 4
    And so to fix this one simply does...? – Mooing Duck Jun 20 '13 at 17:59
  • Provide a user-defined assignment operator taking a volatile argument? – Pixelchemist Jun 20 '13 at 18:04
  • @MooingDuck The only way to do it is user-defined conversion but that is not what I would consider "simple" if the question is about implcit conversion. – Pixelchemist Jun 22 '13 at 01:47
  • I'm pretty sure `X (X const volatile & xo);` is a function that does implicit conversions, making your whole answer quite misleading. – Mooing Duck Jun 22 '13 at 02:19
  • Most of your standards quotes are about implicit or defaulted _functions_, which have virtually nothing to do with implicit _conversions_. – Mooing Duck Jun 22 '13 at 02:20
  • My interpretation of the question was that OP is looking for a way having the compiler dealing with the conversion. A non-explicit constructor with cv-qualified argument will do the trick since the compiler will use it in the assignment (as I pointed out in my answer) to convert the type. – Pixelchemist Jun 22 '13 at 02:22
  • @MooingDuck I nevertheless got your point and restructured my answer for a better compliance with the question. Thank you. – Pixelchemist Jun 22 '13 at 03:05
  • Yeah, sorry about that, I'd misunderstood bits. This answer is actually _really_ good now – Mooing Duck Jun 22 '13 at 03:21
1

Here's how you get a copy constructor and copy assignment that allows a volatile source:

struct X {
  X(const X& o) : members(o.members) {}
  X(const volatile X& o) : members(o.members) {}
  X& operator=(const X& o) {v=o.v; return *this;}
  X& operator=(const volatile X& o) {v=o.v; return *this;}
};

Note though that this has some consequences. The type is no longer POD or even trivially copyable, for one. Which might defeat the whole point of making it volatile.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
0

You can implement the assignment operator =:

T& operator=(const volatile T &rhs) {
    m_x = rhs.m_x;
    return *this;
}
newbie
  • 1,230
  • 1
  • 12
  • 21