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?
clang seems correct to me. I agree with your interpretation that the lambda return type must be
decltype(u)
, but not thatdecltype(u)
isunmovable&
.p19 doesn't apply, since you have
decltype(u)
, notdecltype((u))
.p18 then says that as the
u
indecltype(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 asthen the lambda will return
u
by reference. This will work with clang, if this is the behaviour you were after.