lambda中引用捕获对象的类型

Tob*_*ann 17 c++ lambda decltype language-lawyer

以下代码适用于gcc

#include <map>

int main() {
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        decltype(dict)::value_type bar;
    };
}
Run Code Online (Sandbox Code Playgroud)

但对于msvc我还要另外使用std::remove_reference

#include <map>
#include <type_traits>

int main() {
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        std::remove_reference_t<decltype(dict)>::value_type bar;
    };
}
Run Code Online (Sandbox Code Playgroud)

否则我收到一个错误:

error C2651: 'std::map<int,double,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> &': left of '::' must be a class, struct or union
Run Code Online (Sandbox Code Playgroud)

哪个编译器根据标准显示正确的行为?

更新:

对于msvc decltype(dict)确实是一个参考,如下面的代码

#include <map>

int main()
{
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        decltype(dict) foo;
    };
}
Run Code Online (Sandbox Code Playgroud)

错误

error C2530: 'foo': references must be initialized
Run Code Online (Sandbox Code Playgroud)

如果这确实是错误的行为,它可能会导致令人讨厌的错误,比如使用msvc编译代码时的悬空引用.

#include <map>

std::map<int, double> return_a_map()
{
    std::map<int, double> result;
    return result;
}

int main()
{
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        decltype(dict) foo = return_a_map();
        // foo is a dangling reference in msvc
    };
}
Run Code Online (Sandbox Code Playgroud)

Col*_*mbo 12

关于非括号应用的特殊规则decltype(即[expr.prim.lambda]/20不适用).所以我们回到通常的定义decltype,它要求如果操作数是一个id-expression,则yielding类型只是实体的声明类型,而不是引用类型.因此VC++是错误的.

注意:是否dict被捕获并不重要,因为17:

lambda 表达式复合语句中的每个id表达式(由副本捕获的实体的odr-use(3.2))将转换为对闭包类型的相应未命名数据成员的访问.[ 注意:不是odr-use id表达式是指原始实体,永远不是闭包类型的成员.此外,这样的id表达式不会导致实体的隐式捕获.- 结束说明 ]

decltype永远不要使用任何操作数或子操作.这条规则实际上有时会遇到很多问题,例如核心问题958所示:

int f (int&);
void* f (const int&);

int main()
{
   int i;
   [=] ()-> decltype(f(i)) { return f(i); };
}
Run Code Online (Sandbox Code Playgroud)

这里,decltype(f(i))使用非const i封闭范围.但是,由于lambda不是mutable,i实际上是正文const,因此尾随返回类型是不正确的.CWG总结说,这种情况很少发生,值得解决.