Why am I Not Provided with a Default Copy Constructor from a Volatile

Why am I not provided with a default copy constructor from a volatile?

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.

Why is it impossible to explicitly default a copy constructor with volatile argument?

You are in the right sections, but are overlooking some crucial bullets.

[dcl.fct.def.default]/1:

A function definition of the form:

...

is called an explicitly-defaulted definition. A function that is
explicitly defaulted shall

  • 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

[class.copy.ctor]/7:

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

X::X(const X&)

if each potentially constructed subobject of a class type M (or array
thereof) has a copy constructor whose first parameter is of type const
M& or const volatile M&.119 Otherwise, the implicitly-declared copy
constructor will have the form

X::X(X&)

...

119) This implies that the reference parameter of the implicitly-declared copy constructor cannot bind to a volatile lvalue;

When the above is summed up, your only two options for explicitly defaulting a copy c'tor are these:

struct A {
A(const A&) = default;
};

struct B {
B(B&) = default;
};

When the standard says A(const volatile A&) is a copy constructor. It means that a user-provided c'tor with such a parameter can be the classes copy c'tor.

Copy constructor for C volatile bitfield struct

Just because your struct is marked extern "C" doesn't mean it won't still be compiled as C++ code.

This means that return mci->info->status; invokes the implicitly generated copy constructor.
Because _ARM_MCI_STATUS is marked volatile, it's members are, which means the default copy constructor which takes T& can't bind to the volatile lvalue reference it's passed.

This is explained in the cppreference explanation:

Otherwise, the implicitly-declared copy constructor is T::T(T&). (Note
that due to these rules, the implicitly-declared copy constructor
cannot bind to a volatile lvalue argument.
)

And also in the actual standard (Just having a hard time finding the correct clause but it's in there).

volatile struct = struct not possible, why?

This is ill-formed because FOO has an implicit copy constructor defined as:

FOO(FOO const&);

And you write FOO test = foo; with foo of type volatile FOO, invoking:

FOO(volatile FOO const&);

But references-to-volatile to references-to-non-volatile implicit conversion is ill-formed.

From here, two solutions emerge:

  1. don't make volatile to non-volatile conversions;
  2. define a suited copy constructor or copy the object members "manually";
  3. const_cast can remove the volatile qualifier, but this is undefined behavior to use that if your underlying object is effectively volatile.

Could I possibly use memcopy() for that?

No you cannot, memcpy is incompatible with volatile objects: thre is no overload of it which takes pointers-to-volatile, and there is nothing you can do without invoking undefined behavior.

So, as a conclusion, your best shot if you cannot add a constructor to FOO is to define:

FOO FOO_copy(FOO volatile const& other)
{
FOO result;
result.a = other.a;
result.b = other.b;
result.c = other.c;
return result;
}

Or with C++11's std::tie:

FOO FOO_copy(FOO volatile const& other)
{
FOO result;
std::tie(result.a, result.b, result.c) = std::tie(other.a, other.b, other.c);
return result;
}

Why can't I override the default copy constructor and assignment operator with template versions in C++

template<typename T>
BaseClass(const T& a_other)

First of all, this is not a copy-constructor. It is rather a templated constructor.

The copy-constructor should be this:

BaseClass(const BaseClass & a_other)

Notice the difference?

Note that the templated constructor doesn't define copy-constructor. The compiler will still generate a default copy-constructor for you, instead of instantiating the templated constructor.

Same argument for copy-assignment.

indexing an element from a volatile struct doesn't work in C++

Your program is trying to copy a SensorData_t object. The compiler supplies a copy constructor with the following signature:

SensorData_t(const SensorData_t &)

This copy constructor will not work with volatile arguments, hence the compilation error.

You can write your own copy constructor which works with volatile SensorData_t objects (as well as non-volatile SensorData_t objects):

struct SensorData_t {
SensorData_t() = default;

SensorData_t(const volatile SensorData_t &other)
: test(other.test) {
}

int test;
};

Conditions under which compiler will not define implicits (constructor, destructor, copy constructor, copy assignment)

The Default Constuctor (e.g., X()) will not be implicitly generated if:

  • you have explicitly declared any constructor at all
  • there is a data member that is not default-constructible (such as a reference, a const object, or a class with no or inaccessible default constructor)
  • (C++11) you have explicitly told the compiler to not generate one using X() = delete;

The Copy Constructor (e.g., X(const X&)) will not be implicitly generated if:

  • you have explicitly declared a copy constructor (for class X a constructor taking X, X& or const X&)
  • there is a data member that is not copy-constructible (such as a class with no or inaccessible copy constructor)
  • the base class is not copy-constructible
  • (C++11) you have declared a move constructor or move assignment operator
  • (C++11) you have explicitly told the compiler to not generate one using X(const X&) = delete;

The Copy Assignment Operator (e.g., X& operator=(const X&)) will not be implicitly generated if:

  • you have explicitly declared a copy assignment operator (for class X an operator= taking X, X& or const X&)
  • there is a data member in your class that is not copy-assignable (such as a reference, a const object, or a class with no or inaccessible assignment operator)
  • the base class is not copy-assignable
  • (C++11) you have declared a move constructor or move assignment operator
  • (C++11) you have explicitly told the compiler to not generate one using X& operator=(const X&) = delete;

The Destructor (e.g., ~X()) will not be implicitly generated if:

  • you have explicitly declared a destructor
  • (C++11) you have explicitly told the compiler to not generate one using ~X() = delete;

The Move Constructor (C++11) (e.g., X(X&&)) will not be implicitly generated if:

  • you have explicitly declared a move constructor (for class X, a constructor taking X&&)
  • you have declared a copy assignment operator, copy constructor, destructor, or move assignment operator
  • there is a data member in your class that cannot be move-constructed (is const, is a reference, or has a deleted, inaccessible, or ambiguous move constructor)
  • the base class cannot be move-constructed
  • you have explicitly told the compiler to not generate one using X(X&&) = delete;

The Move Assignment Operator (C++11) (e.g., X& operator=(X&&)) will not be implicitly generated if:

  • you have explicitly declared a move assignment operator (for class X, an operator= taking X&&)
  • you have declared a copy assignment operator, copy constructor, destructor, or move constructor
  • there is a data member in your class that cannot be move-assigned (is const, is a reference, or has a deleted, inaccessible, or ambiguous move assignment operator)
  • the base class cannot be move-assigned
  • you have explicitly told the compiler to not generate one using X& operator=(X&&) = delete;

Why doesn't the standard consider a template constructor as a copy constructor?

Let's put templates aside for a second. If a class doesn't declare a copy constructor, an implicitly defaulted one is generated. It may be defined as deleted, but it's defaulted nonetheless.

A member template is not a member function. Members are instantiated from it only when needed.

So how can a compiler know from the class definition alone whether or not a specialization with T = Foo will ever be needed? It can't. But it's exactly that on which it needs to base a decision of how to handle a potential need for an implicitly defaulted copy constructor (AND move constructor). That becomes messy.

The easiest approach is to exclude templates. We'll always have some copy constructor anyway, it will do the correct thingTM by default, and will be favored by overload resolution because it's not instantiated from a template.



Related Topics



Leave a reply



Submit