Reputation: 263
I have compilers disagreeing on a small C++14 code snippet:
#include <cassert>
struct unmovable {
unmovable() {}
unmovable(unmovable&&) = delete;
};
int main()
{
unmovable u;
auto i = [&]() -> decltype(auto) { return u; };
auto& uu = i();
assert(&uu == &u);
}
The program is accepted by g++4.9.3, g++-5.1.0, g++-5.2.0 and VisualStudio 2015, but not by clang++-3.7.
clang++-3.7 deduces the return type to be unmovable
(value) and not unmovable&
.
If the program is changed slightly, so that the variable u
is global, then all compilers agree on the error.
As I understand it, the captured u
in the lambda should be of type unmovable&
when the variable is local.
I don't have the C++14 standard, but hopefully the draft from github is relevant. My interpretation of 7.1.6.2 and 7.1.6.4 is that decltype(auto)
becomes decltype(u)
from the return, which in the global case should be unmovable
(value) and in the lambda reference capture of the local u
, it should become unmovable&
since the captured variable must be of type unmovable&
. This would indicate that clang++ got it wrong.
If I change the lambda and its use slightly:
auto i = [](auto& v) -> decltype(auto) { return v; };
auto& uu = i(u);
then all compilers accept it, regardless of whether u
is global or local, which I think strengthens my interpretation of the decltype(auto)
deduction, since v
here definitely becomes of type unmovable&
.
Is my interpretation correct and thus clang++ incorrect?
Upvotes: 11
Views: 3574
Reputation:
clang seems correct to me. I agree with your interpretation that the lambda return type must be decltype(u)
, but not that decltype(u)
is unmovable&
.
5.1.2 Lambda expressions [expr.prim.lambda]
18 Every id-expression within the compound-statement of a lambda-expression that is an odr-use (3.2) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [ Note: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. -- end note ] [...]
19 Every occurrence of
decltype((x))
where [...]
p19 doesn't apply, since you have decltype(u)
, not decltype((u))
.
p18 then says that as the u
in decltype(u)
is not an odr-use, it refers to the original entity, it does not get transformed to an access of the member of the closure type.
However, p19 does make it clear that if you write your return
statement as
auto i = [&]() -> decltype(auto) { return (u); };
then the lambda will return u
by reference. This will work with clang, if this is the behaviour you were after.
Upvotes: 8