我让编译器在一个小的C ++ 14代码片段上不同意:
#include <cassert>
struct unmovable {
unmovable() {}
unmovable(unmovable&&) = delete;
};
int main()
{
unmovable u;
auto i = [&]() -> decltype(auto) { return u; };
auto& uu = i();
assert(&uu == &u);
}
该程序被g ++ 4.9.3,g ++ - 5.1.0,g ++ - 5.2.0和VisualStudio 2015接受,但不是由clang ++ - 3.7接受。
clang ++ - 3.7推断返回类型为unmovable
(值)而不是unmovable&
。
如果稍微改变程序,以便变量u
是全局的,那么所有编译器都会同意错误。
据我了解,当变量是本地变量时,lambda中捕获的u
应该是unmovable&
类型。
我没有C ++ 14标准,但希望github的草案是相关的。我对7.1.6.2和7.1.6.4的解释是decltype(auto)
从返回变为decltype(u)
,在全局情况下应该是unmovable
(值)并且在lambda参考捕获中本地u
,它应该变为unmovable&
,因为捕获的变量必须是unmovable&
类型。这表明clang ++错了。
如果我稍微更改lambda及其用法:
auto i = [](auto& v) -> decltype(auto) { return v; };
auto& uu = i(u);
然后所有编译器都接受它,无论u
是全局的还是本地的,我认为这加强了我对decltype(auto)
推论的解释,因为v
这里肯定变成类型{{ 1}}。
我的解释是否正确,因此clang ++不正确?
答案 0 :(得分:7)
decltype(u)
,但decltype(u)
不是unmovable&
。
5.1.2 Lambda表达式[expr.prim.lambda]
18 lambda-expression 的复合语句中的每个 id-expression ,它是一个odr-use(3.2)的 通过副本捕获的实体转换为对相应的未命名数据成员的访问权限 闭合类型。 [注意: id-expression 不是odr-use,是指原始实体,永远不是闭包类型的成员。此外,这样的 id-expression 不会导致实体的隐式捕获。 - 结束记录] [...]
19
decltype((x))
每次出现[...]
p19不适用,因为您有decltype(u)
,而不是decltype((u))
。
p18然后说,因为u
中的decltype(u)
不是odr-use,它指的是原始实体,它不会转换为闭包类型成员的访问。
但是,如果您将return
语句写为
auto i = [&]() -> decltype(auto) { return (u); };
然后lambda将通过引用返回u
。这将适用于clang,如果这是你所追求的行为。