Can a Copy-Constructor Take a Non-Const Parameter

Can a copy constructor have a non-const lvalue parameter?

The problem is that the add member function returns an rvalue expression of type Complex and you're trying to bind a non-const lvalue reference Complex& to that rvalue.

You can solve this error by replacing Complex(Complex &c) with:

Complex(const Complex &c) //const added here

Note the const added in the above statement. Now, the parameter of the copy constructor is a reference to const Complex which can be bound to an rvalue.

Non-const copy constructor compiles fine with C++17

fun() is a prvalue of type A, so A a = fun(); means that a is the result object of the function call, there is no intermediate temporary.

The text for this is in C++17 [basic.lval]/2:

The result object of a prvalue is the object initialized
by the prvalue;

It would be the same for A a = A(A(A(A(A(fun()))))); etc. - all of the prvalues have a as their result object.

The behaviour of the return statement is in [stmt.return]/2:

the return statement initializes the glvalue result or prvalue result object of the (explicit or implicit) function
call by copy-initialization (11.6) from the operand.

The result object can be initialized successfully by copy-initialization from a (the local variable of fun) because that is a non-const lvalue and so the copy constructor taking non-const lvalue reference does bind to it.


Prior to C++17 fun()'s return value was a temporary object, and then main's a was copy/move-constructed from the temporary, with elision being optional (but the valid constructor was required to exist).

why templated non const parameter constructor is preferred to given copy constructor

The problem is not with std::variant. The problem is with the constructor template,

template <typename T>
A(T& t)

Such constructors are problematic because when the argument is a non-const lvalue of type A, this constructor is preferred over the copy constructor taking const A&---which is usually not the intended behaviour. To prevent this, we usually constrain this constructor with SFINAE:

template <typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, A>>>
A(T& t) // or T&& t

and might consider making it explicit as well.

We usually do not provide copy constructors taking non-const A&, since they are redundant next to the ones taking const A&.

Copy constructor converts from const to non-const?

No idea what you mean. A(const A&) is a typical copy-ctor, which has a "read-only" access to its only argument. If you pass anything const, everything is fine. If you pass anything non-const, for ctor it becomes const. A a = 1 is a conversion ctor, as you said. A other = a is a copy ctor. What's the question?

Regarding your question's title, in C++ there's no fair way to convert const to non-const.

class A
{
public:
int xx;
A(const A& other)
{
cout << "A cctor" << endl;
/* do some stuff */
// other is const here - you can only call its
// const methods and read all its data members
}

A(int x) : xx(x) {} /* conversion constructor */
// at this point, x is not const, but it's a copy
// of an object you've passed here, not the object itself
// you can change x, it just doesn't matter - you
// change the copy
};

int main()
{
A a = 1; // a is not const, 1 is passed "by value", since it's primitive type
A other = a; // a is not const, other is not const, a is passed by const reference
return 0;
}

Non-const copy constructor

In your case, you probably should use the mutable keyword.

Your object would still remain logically const because, from the user point of view, nothing has changed.



Related Topics



Leave a reply



Submit