2

I have coded a very basic class

class A
{
 int n;
 public:
 A(int& val){val=n;}
 A(const int& val=0){n=val;}
 A(A& val){n=val.n;}//work without this constructor
};

int main()
{
 A a=3;//want to call A::A(const int&)
 return 0;
}

I don't want to create a constructor with a copy from an instance of A (for a future use) What's wrong with this simple code?

Error message :

...\main.cpp||In function 'int main()':|
...\main.cpp|16|error: no matching function for call to 'A::A(A)'|
...\main.cpp|16|note: candidates are:|
...\main.cpp|11|note: A::A(A&)|
...\main.cpp|11|note:   no known conversion for argument 1 from 'A' to 'A&'|
...\main.cpp|10|note: A::A(const int&)|
...\main.cpp|10|note:   no known conversion for argument 1 from 'A' to 'const int&'|
...\main.cpp|9|note: A::A(int&)|
...\main.cpp|9|note:   no known conversion for argument 1 from 'A' to 'int&'|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

It seems 3 is considered as an instance of A?

If I add A(const A& val){n=val.n;} the constructor A(const int&) is chosen.

What can I do to compile successfully without A(const A& val)?

Niall
  • 30,036
  • 10
  • 99
  • 142
simple_guy
  • 31
  • 5

3 Answers3

7

The issue is your copy constructor: A(A& val) { n=val.n; }.

Given this line A a = 3; one possibility is to use A a = A(3) which in turn will use the copy constructor. However, it is against the standard to bind a temporary to non-const. A proper copy constructor will solve the problem.

Example Code

#include <iostream>

class A
{
    int n;

public:
    A(int& val) : n(val) { std::cout << "A(int&)\n"; }
    A(const int& val=0) : n(val) { std::cout << "A(const int&)\n"; }
    A(const A& val) : n(val.n) { std::cout << "A(const A&)\n"; }
};

int main()
{
    A a = 3;
    return 0;
}

Example Output

A(const int&)

Live Example

Note:

  • The above code makes proper use of the initialization lists
  • The output shows the copy constructor is not actually invoked
James Adkison
  • 9,412
  • 2
  • 29
  • 43
  • 3
    What are `A(int& val)` and `A(const int& val=0)` about, though? – LogicStuff Feb 04 '16 at 14:44
  • 1
    @LogicStuff I don't know why there is a constructor for `int&`, it was the OPs code. I would think the `const int&` one is sufficient. I would think they should also be `explicit` but that would disallow the implicit conversion being done (i.e., `A a = 3;`). – James Adkison Feb 04 '16 at 14:47
  • Or pass primitives by value. – LogicStuff Feb 04 '16 at 14:49
  • 1
    @LogicStuff Yes, of course by value would do well (and may be arguably preferred). – James Adkison Feb 04 '16 at 14:51
  • If I add A(const A&) it's work ( as specified in my first post) And `A a=3 call `A(const int&)` constructor Here it's a simple example that cause error. I don't want work on a copy of my instance for test I use stat variables and one of them is the number of use of my instance. So I need change this (wich it's not possible with a copie) – simple_guy Feb 04 '16 at 14:51
  • 2
    @simple_guy You need the correct copy constructor for it to work. However, my understanding is that doesn't mean a copy will actually be used thanks to RVO. – James Adkison Feb 04 '16 at 14:53
2

The line of code;

A a = 3;

Is using copy initialisation to work. For that to work, a copy of A is needed and is duly made. You have two constructors taking an int as an argument (and neither are explicit).

The copy cannot bind to A(A& val) because normal references don't bind to temporary values.

Possible solutions, direct initialisation;

 A a { 3 };

Add or change the copy constructor to a move constructor;

A(A&& val);

Add a const to the copy constructor (although this is not what you wanted, but it does work);

A(A const& val);

Note both clang and g++ reject the original code correctly, but VC++ accepts it.

Niall
  • 30,036
  • 10
  • 99
  • 142
  • 1
    MSVC is wrong. See [Non-const reference bound to temporary, Visual Studio bug?](http://stackoverflow.com/questions/16380966/non-const-reference-bound-to-temporary-visual-studio-bug) – NathanOliver Feb 04 '16 at 15:06
  • @NathanOliver. I know about that, it also warns you these days if you do that. It gives no warning in this case. – Niall Feb 04 '16 at 15:08
  • As [cppreference](http://en.cppreference.com/w/cpp/language/copy_initialization) said, "The result of the conversion, which is a prvalue temporary if a converting constructor was used, is then used to direct-initialize the object." This direct-initialization step requires a ctor that can accept a rvalue. – cpplearner Feb 04 '16 at 15:30
  • @cpplearner. I see that now, makes sense when read together with the other quote – Niall Feb 04 '16 at 17:24
2

In this statement

A a=3;//want to call A::A(const int&)

there is created a temporary object of type A using constructor

A(const int& val=0){n=val;}

However a temporary object can be bound to a constant reference. So the compiler need that the copy constructor

A( const A& val){n=val.n;}//work without this constructor
   ^^^^^

would be at least accessible (even if it will not be called due to the copy constructor elision). But the class does not has this constructor and the compiler issues an error.

On the other hand when you comment this constructor then the compiler itself implicitly defines this constructor and the code is compiled.

Take into account that this constructor

A(int& val){val=n;}
            ^^^^^^

does not make sense.:)

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335