Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lvalue decaying to rvalue with auto error

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.

like image 466
Robert Mason Avatar asked Dec 15 '14 19:12

Robert Mason


1 Answers

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.

like image 185
Columbo Avatar answered Oct 13 '22 10:10

Columbo