Whats the meaning of gcc/g++ error 'implicitly-declared' constructor is 'deprecated'?

Viewed 1771

I am trying to chase down errors coming out of upgrading my build system OS and gcc version with it to 9. I can demonstrate it with the following code.

class Cl {
        private:
                float f;
        public:
        constexpr float GetF() const { return f; }
        Cl& operator=(const Cl& other) {
                f = other.GetF();
                return *this;
        }
        Cl& operator=(const float& other) {
                this->f = other;
                return *this;
        }
        explicit constexpr Cl(const float& val) : f(val) {}
};

struct Sl {
        float x, y;
        Cl lcl;
        constexpr Sl(const float &init_x, const float &init_y, const Cl &cl) : x(init_x), y(init_y), lcl(cl) {}

};

typedef struct Sl sdata;

int main()
{
        const float fx = 30.30;
        Cl c1(fx);
        sdata s1(0, 0.0, c1);
        return 0;
}

Compiled with:

preetam@preetam-Precision-M4800 ~ $ g++-9 -Werror=deprecated-copy dp_test.cc 
dp_test.cc: In constructor ‘constexpr Sl::Sl(const float&, const float&, const Cl&)’:
dp_test.cc:22:101: error: implicitly-declared ‘constexpr Cl::Cl(const Cl&)’ is deprecated [-Werror=deprecated-copy]
   22 |  constexpr Sl(const float &init_x, const float &init_y, const Cl &cl) : x(init_x), y(init_y), lcl(cl) {}
      |                                                                                                     ^
dp_test.cc:8:6: note: because ‘Cl’ has user-provided ‘Cl& Cl::operator=(const Cl&)’
    8 |  Cl& operator=(const Cl& other) {
      |      ^~~~~~~~
cc1plus: some warnings being treated as errors`

Whats is the meaning of the error ?

I tried adding the following to Cl as suggested here:

constexpr Cl(const Cl& other) {
    f = other.GetF();
}

It resulted in the error:

preetam@preetam-Precision-M4800 ~ $ g++-9 -Werror=deprecated-copy dp_test.cc 
dp_test.cc: In copy constructor ‘constexpr Cl::Cl(const Cl&)’:
dp_test.cc:10:9: error: member ‘Cl::f’ must be initialized by mem-initializer in ‘constexpr’ constructor
   10 |         }
      |         ^
dp_test.cc:5:9: note: declared here
    5 |   float f;
      |         ^

Finally Fixed with the following copy constructor:

constexpr Cl(const Cl& other) : f(other.f) {}
2 Answers

You are using a copy-constructor for lcl(cl) although you didn't define one but you defined a user-defined assignment operator. You can fix it by adding a user-defined constructor:

class Cl {
        private:
                float f;
        public:
        constexpr float GetF() const { return f; }
        constexpr Cl(const Cl& other) : f(other.f) { }
        Cl& operator=(const Cl& other) {
                f = other.GetF();
                return *this;
        }
        Cl& operator=(const float& other) {
                this->f = other;
                return *this;
        }
        explicit constexpr Cl(const float& val) : f(val) {}
};

struct Sl {
        float x, y;
        Cl lcl;
        constexpr Sl(const float &init_x, const float &init_y, const Cl &cl) : x(init_x), y(init_y), lcl(cl) {}

};

typedef struct Sl sdata;

int main()
{
        const float fx = 30.30;
        Cl c1(fx);
        sdata s1(0, 0.0, c1);
        return 0;
}

Nitpick: "Whats is the meaning of the error ?" It's not an error but a warning. The project can be compiled and should run as expected but of course, the problem should be fixed.

The lcl member of the Sl struct is initialised in the constructor of Sl. This initialisation implies the usage of the copy constructor.

However, as stated by the message of the compiler, if we provide our own version of the copy-assign operator, this certainly means that there is something subtle in such a copy operation, then the copy-constructor should have probably been explicitly provided too.

If no copy-assign operator is provided, then the default one is considered as suitable, and the default copy-constructor is probably suitable too.

Note that you should always consider the rule of five or zero.

Related