5

I apologize if the question title is inaccurate - but I am having difficulties understanding what is going on here.

Consider the following class:

struct foo {
    foo(foo&);
};

The following has no errors:

void func(foo& f) {
    foo bar{f};
}

However, when I use auto:

void func(foo& f) {
    auto bar = foo{f};
}

I get (gcc):

test.cpp: In function ‘void func(foo&)’:
test.cpp:6:21: error: no matching function for call to ‘foo::foo(foo)’
test.cpp:6:21: note: candidate is:
test.cpp:2:5: note: foo::foo(foo&)
test.cpp:2:5: note:   no known conversion for argument 1 from ‘foo’ to ‘foo&’

(clang)

test.cpp:6:10: error: no matching constructor for initialization of 'bar'
    auto bar = foo{f};
         ^     ~~~~~~
test.cpp:2:5: note: candidate constructor not viable: expects an l-value for 1st argument
    foo(foo&);
    ^

Can somebody please explain why this is an error?

Thanks!

Edit: It works if I add a copy-constructor to foo. However, I was under the impression that the variable declaration + explicit call to constructor on the right side of the '=' syntax is treated specially and is not a copy-construction but rather a direct initialization.

Robert Mason
  • 3,949
  • 3
  • 31
  • 44

2 Answers2

8
auto bar = foo{f};

auto is deduced as foo. Subsequently, your definition is equivalent to

foo bar = foo{f};

You are trying to create an object of type foo that is copy-initialized with the prvalue foo{f}.

The problem is that the copy constructor of foo has a non-const lvalue reference as its parameter, which is impossible to bind to an rvalue. Also, the move constructor isn't implicitly defined as you have a user-declared copy constructor. Hence there is no constructor that can take foo{f}, and the compiler issues an error message.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Will the compiler emit a call to the copy constructor? I was under the impression that the compiler would treat this as a direct initialization. – Robert Mason Dec 15 '14 at 19:56
  • Ahh typing on android is slow, and there seems to be no way to delete the answer as well – Anton Savin Dec 15 '14 at 19:57
  • @RobertMason The compiler may elide a call to a copy constructor which has no side-effects, but the copy constructor must still be callable. – Oktalist Dec 15 '14 at 19:59
  • @RobertMason Regardless of whether the call would be *elided*, the constructor must be invoke-able and produce a valid call. – Columbo Dec 15 '14 at 20:00
3
auto bar = foo{f};

This is copy-initialization, and by its semantics it requires the presence of copy constructor. However, the copy constructor of foo takes non-const reference, and non-const reference cannot be bound to temporary, that is foo{f}.

The solution is to make a copy constructor which takes const reference, as copy constructors usually do.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90