c++copy-assignment

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?


Solution

  • 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.