skip to Main Content

Question:

I am learning c++, and i created a class to represent complex numbers. I created a copy constructor with the format

complex(const complex &c);

and the program worked fine.

Then, i removed the const (so it became: complex( complex &c); ) and the program doesn’t work. It gives me this error:

"message": "class "complex" has no suitable copy constructor"

and it refers to the add method. The add method is this:

complex complex::add(complex &c){
    return complex( re + c.re, im + c.im);
};

If i do not add this method (and methods that generally return a complex number that is created in the return line, like this: return complex (integer_variable_Re, integer_variable_Im) ) the program works fine. When i add these kinds of methods, it throws me the error. I cannot understand why this doesn’t work, as it should call the constructor that takes two integers and not the copy constructor.

Specs:
Ubuntu: 20.04Lts

IDE: VScode

Compliler: G++ 9.4

(it says somewhere this: GNU C17 (Ubuntu 9.4.0-1ubuntu1~20.04.1) version 9.4.0 (x86_64-linux-gnu) compiled by GNU C version 9.4.0, GMP version 6.2.0, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.22.1-GMP, do not know if it it the compliler standard.)

Whole error message:
[{ "resource": "/home/papaveneti/Documents/Programming/LearnCpp/complex_classStack.cpp", "owner": "C/C++", "code": "334", "severity": 8, "message": "class "complex" has no suitable copy constructor", "source": "C/C++", "startLineNumber": 48, "startColumn": 12, "endLineNumber": 48, "endColumn": 19 }]

Whole program:

#include <iostream>
#include <math.h>

using namespace std;

class complex {

public:
    complex (double r, double i); // constructor
    // only method that does not specify type
    
    //default constructor
    complex (); 

    //copy cosntructor
    complex( complex &c);

    // methods: 
    complex add(complex &c);
    void norm1();

private:
    // fields:
    double re, im; 
    double norm;
};

//constructor does not need to name a type
complex::complex(double r=0.0, double i=0.0){ // default values are 0
    re = r; im = i; 
    norm1();
};

complex::complex(){
    re=im=0;
}

complex::complex( complex &c){
    re=c.re;
    im=c.im;
}

void complex::norm1(){
    norm = sqrt(re*re + im*im);
};

complex complex::add(complex &c){
    return complex( re + c.re, im + c.im);
};

int main(){
    complex c1(3,4), c2(1,2);

    return 0;
}

2

Answers


  1. Before C++17, the following line

    return complex( re + c.re, im + c.im);
    

    actually includes two object creations. First an unnamed temporary object is constructed using two double parameters. Then a copy of this object is made to where the return value is stored. This second copy is the reason for your compiler error. A copy constructor can only be used to copy a temporary object, if it is declared like this:

    complex(const complex&);
    

    If you omit the const qualifier, you cannot pass a temporary object.

    With the introduction of mandatory copy elision in C++17, there is no copy (or move) involved in the line mentioned above. Thus your program compiles fine. Your compiler, which is rather old, does not use C++17 by default and so refuses to compile the program.

    To solve your problem, you could either

    • use a copy constructor which takes a const reference
    • add a move constructor
    • use C++17 (either by using a newer compiler version or by passing the --std=c++17 command line option to your compiler

    Edit:
    You should also fix the problem that @Jason Liam mentions in this answer. As mentioned there, your program is ill-formed and should not compile even with the const constructor.

    Login or Signup to reply.
  2. The program is ill-formed in all C++ versions and gcc and msvc are wrong in accepting the code because while defining the constructor complex::complex(double r, double) outside the class you’re providing default arguments for both of the parameters which turns it into a default constructor. This is not allowed and clang rejects it.

    From default arguments documentation:

    For a member function of a non-template class, the default arguments are allowed on the out-of-class definition, and are combined with the default arguments provided by the declaration inside the class body. If these out-of-class defaults would turn a member function into a default constructor or copy/move (since C++11) constructor/assignment operator, the program is ill-formed

    (emphasis mine)

    To solve this you have to make sure that you don’t add these default arguments while implementing the ctor outside the class so that you avoid turning it into a default ctor. Additionally, if you’re using C++11 or C++14 then you must add a low-level const to the parameter of the copy ctor as there was no mandatory copy elision prior to C++17.


    Here is the gcc bug:

    GCC accepts ill-formed out of class definition program

    Here is the msvc bug:

    MSVC accepts invalid program involving default arguments inside constructor

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search