从lambda捕获

时间:2015-11-22 11:46:52

标签: c++ c++11 lambda c++14 decltype

我让编译器在一个小的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 ++不正确?

1 个答案:

答案 0 :(得分:7)

对我来说,铿锵似乎是正确的。我同意你的解释,lambda返回类型必须是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语句写为

,p19会明确说明
auto i = [&]() -> decltype(auto) { return (u); };

然后lambda将通过引用返回u。这将适用于clang,如果这是你所追求的行为。