Lambda内引用捕获对象的类型 [英] type of reference-captured object inside lambda

查看:391
本文介绍了Lambda内引用捕获对象的类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码可用于 gcc

#include <map>

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

但是对于 msvc ,我必须另外使用std::remove_reference

But for msvc I have to additionally use 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;
    };
}

否则,我会收到错误:

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

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

Which compiler shows the correct behaviour according to the standard?

更新:

对于 msvc decltype(dict)确实是参考,如以下代码

For msvc decltype(dict) really is a reference, as the following code

#include <map>

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

错误

error C2530: 'foo': references must be initialized

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

If this really is wrong behaviour, it could lead to nasty bugs, like dangling references when code is compiled with 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
    };
}

推荐答案

对于decltype的非括号应用程序,没有特殊的规则(即[expr.prim.lambda]/20 不适用).因此,我们回到常规的decltype定义,该定义要求,如果操作数是 id-expression ,则产生的类型只是实体的声明类型,而不是引用类型.因此,VC ++是错误的.

There is no special rule regarding non parenthesized applications of decltype (ie. [expr.prim.lambda]/20 does not apply). So we just fall back to the usual definition of decltype, which mandates that if the operand is an id-expression, the yielded type is just the declared type of the entity, and that's not a reference type. Hence VC++ is wrong.

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

NB: it doesn't matter whether dict is captured or not, because ¶17:

lambda-expression compound-statement 中的每个 id-expression 都是奇特用途(3.2) (通过副本捕获的实体)将转换为对相应的未命名数据成员的访问. 闭合类型. [ Note :不是odr-use的 id-expression 指的是原始实体, 封闭类型.此外,这样的 id-expression 不会导致隐式捕获实体. —结束 注意]

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 ]

decltype从不odr-使用其任何操作数或子操作数.有时,例如,这条规则实际上是非常成问题的.如核心问题958 所示:

decltype never odr-uses any of its operands or suboperands. This rule actually gets pretty problematic at times, e.g. as shown in core issue 958:

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

int main()
{
   int i;
   [=] ()-> decltype(f(i)) { return f(i); };
}

在这里,decltype(f(i))使用封闭范围中的非const i.但是,由于lambda不是mutable,因此主体中的i实际上是const,因此尾随返回类型是错误的. CWG认为这种情况很少出现,因此不值得解决.

Here, decltype(f(i)) uses the non-const i from the enclosing scope. However, since the lambda isn't mutable, the i in the body is actually const, hence the trailing-return-type is incorrect. CWG concluded this arises too infrequently to be worth solving.

这篇关于Lambda内引用捕获对象的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆