Dimitrije Kostic
Dimitrije Kostic

Reputation: 305

In C++, can a class with a const data member not have a copy assignment operator?

I'm designing a class that ought to have a const data member called K. I also want this class to have a copy assignment operator, but the compiler seems to implicitly delete the copy assignment operator from any class with const data members. This code illustrates the essential problem:

class A
{
    private:
       const int K;
    public:
       A(int k) : K(k) {} // constructor
       A() = delete; // delete default constructor, since we have to set K at initialization

       A & operator=(A const & in) { K = in.K; } // copy assignment operator that generates the error below
}

Here's the error it generates:

constructor.cpp:13:35: error: cannot assign to non-static data member 'K' with const- 
qualified type 'const int'
            A & operator=(A const & in) { K = in.K; }
                                          ~ ^
constructor.cpp:6:13: note: non-static data member 'K' declared const here
            const int K;
            ~~~~~~~~~~^
1 error generated.

I think I understand why the compiler does this; the instance of the class I'd want to copy to has to exist before it can be copied to, and I can't assign to K in that target instance if it's const, as I'm trying to do above.

Is my understanding of this problem correct? And if so, is there a way around this problem? That is, can I define a copy constructor for my class and still give K const-like protection?

Upvotes: 6

Views: 3525

Answers (2)

doug
doug

Reputation: 4289

As of c++20, you can now copy objects that have one or more const member objects by defining your own copy-assignment operator.

class A
{
private:
    const int K;
public:
    A(int k) : K(k) {} // constructor
    A() = delete; // delete default constructor, since we have to set K at initialization
    // valid copy assignment operator in >= c++20
    A& operator=(A const& in) {
        if (this != &in)
        {
            std::destroy_at(this);
            std::construct_at(this, in);
        }
        return *this;
    }
};

This was made possible by changes in basic.life which allows transparent replacement of objects, including those containing const sub-objects, w/o UB.

Upvotes: 9

JohnFilleau
JohnFilleau

Reputation: 4288

In C++, a class with a const data member may have a copy constructor.

#include <iostream>

class A
{
private:
    const int k_;
public:
    A(int k) : k_(k) {}
    A() = delete;
    A(const A& other) : k_(other.k_) {}

    int get_k() const { return k_; }
};

int main(int argc, char** argv)
{
    A a1(5);
    A a2(a1);

    std::cout << "a1.k_ = " << a1.get_k() << "\n";
    std::cout << "a2.k_ = " << a2.get_k() << "\n";
}

Output:

a1.k_ = 5
a2.k_ = 5

In C++, a class with a const data member may not use the default assignment operator.

class A
{
private:
    const int k_;
public:
    A(int k) : k_(k) {}
    A() = delete;
    A(const A& other) : k_(other.k_) {}

    int get_k() const { return k_; }
};

int main(int argc, char** argv)
{
    A a1(5);
    A a2(0);

    a2 = a1;
}

Yields a compile time error:

const_copy_constructor.cpp: In function ‘int main(int, char**)’:
const_copy_constructor.cpp:18:10: error: use of deleted function ‘A& A::operator=(const A&)’
   18 |     a2 = a1;
      |          ^~
const_copy_constructor.cpp:1:7: note: ‘A& A::operator=(const A&)’ is implicitly deleted because the default definition would be ill-formed:
    1 | class A
      |       ^
const_copy_constructor.cpp:1:7: error: non-static const member ‘const int A::k_’, can’t use default assignment operator

In C++, a class with a const data member may use a non-default assignment operator as long as you don't attempt to change the const data member, but you better think long and hard about what it means to use this assignment operator if one of the underlying members cannot be modified.

class A
{
private:
    const int k_;
public:
    A(int k) : k_(k) {}
    A() = delete;
    A(const A& other) : k_(other.k_) {}

    A& operator=(A const& other)
    {
        // do nothing
        return *this;
    }

    int get_k() const { return k_; }
};

int main(int argc, char** argv)
{
    A a1(5);
    A a2(0);

    a2 = a1;
}

Yields no compile time errors.

Upvotes: 9

Related Questions