Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在右值容器中迭代_C++_Loops_C++11_Rvalue_Boost Foreach - Fatal编程技术网

C++ 在右值容器中迭代

C++ 在右值容器中迭代,c++,loops,c++11,rvalue,boost-foreach,C++,Loops,C++11,Rvalue,Boost Foreach,以下代码是否导致未定义的行为 std::map<int, vector<int>> foo() { return ... } BOOST_FOREACH(const int& i, foo()[42]) { std::cout << i << std::endl; } std::map foo() { 返回。。。 } BOOST_FOREACH(const int&i,foo()[42]) { 标准::cout来自: 迭代按值返回序列(

以下代码是否导致未定义的行为

std::map<int, vector<int>> foo()
{
return ...
}

BOOST_FOREACH(const int& i, foo()[42])
{
std::cout << i << std::endl;
}
std::map foo()
{
返回。。。
}
BOOST_FOREACH(const int&i,foo()[42])
{
标准::cout来自:

迭代按值返回序列(即右值)的表达式:

foo()
temporary的寿命没有延长

您可以通过延长
foo
临时生存期来解决这个问题

auto&& m = foo();

for (const int& i : m[42]) {
    std::cout << i << std::endl;
}
auto&&m=foo();
for(const int&i:m[42]){

std::cout返回值一直存在,直到完整表达式结束 这就产生了它。所以这一切都取决于如何
BOOST\u FOREACH
扩展;如果它在for循环之外创建了一个作用域,则 将返回值复制到其中的变量(或使用它 初始化一个引用),那么你就安全了。如果它不安全, 你不是

循环的C++11范围基本上具有绑定的语义 在经典for循环之外的作用域中引用,因此 应该是安全的

编辑:

如果您捕获的是
foo
。正如本杰明·林德利指出的那样,你不是。你是 在地图上捕获
操作符[]
的返回值。这 不是临时的;它是引用。因此 寿命发生,既不在每小时的升压范围内,也不在的范围内。 这意味着地图本身将在游戏结束时被销毁 包含函数调用的完整表达式,以及 未定义的行为会发生。(我想Boost可能会产生深刻的影响。) 地图的副本,这样你就安全了。但不知怎的,我怀疑它 是的。)

编辑结束:

尽管如此,我还是会质疑退回一张支票是否明智
std::map
当您只需要其中的一个条目时。如果 映射实际上存在于函数外部(不在堆上), 然后我会返回一个引用,否则我会找到一些
不幸的是,这很可能是未定义的行为

问题是这里有两个级别:

  • std::map
    是一个r值,其生存期将扩展到完整表达式的结尾
  • std::vector&
    是一个l值引用(指向一个对象),它的生命周期是该对象的生命周期
  • 出现问题的原因是代码(大致)扩展为:

    // from
    for (<init>: <expr>) {
        <body>
    }
    
    // to
    auto&& __container = <expr>;
    for (auto __it = begin(container), __e = end(container); __it != __e; ++__it)
    {
        <init> = *__it;
        <body>
    }
    
    如果它只是
    foo()
    ,这将起作用,因为
    std::map
    的生存期将被延长以匹配
    \uu容器的生存期,但是在这种情况下,我们得到:

    // non-standard gcc extension, very handy to model temporaries:
    std::vector<int>& __container = { std::map<...> m = foo(); m[42] };
    
    //非标准gcc扩展,非常方便建模临时对象:
    std::vector&u container={std::map m=foo();m[42]};
    

    因此,
    \uu容器
    最终指向下面。

    为什么你认为这里会有UB?我在类似的代码中看到内存损坏。但不确定是否是因为这种用法。请查看。似乎有一些宏魔法可以检测参数是否为右值,在这种情况下,它会复制参数。@user2079303:unform单独地,
    std::map::operator[]
    返回一个l值引用(到一个临时对象中);而不是一个r值;因此
    map
    temporary的生存期没有正确地延长,我们最终得到了一个到虚空的引用。他所做的事情并不简单。
    operator[]
    在映射上,它不是按值返回,而是返回一个引用。你确定它应该在循环的范围内工作吗?@BenjaminLindley标准似乎是这样说的。在§6.5.4中,它给出了基于范围的等价代码,并且在那里,初始化表达式绑定到引用,因此它的生存期应该延长以匹配t引用的帽子。(当然,考虑到这是一个新特性,我不相信任何编译器都能100%正确地完成它。)@BenjaminLindley,但正如您在对另一个答案的评论中指出的,我刚刚读到的,他不是用函数的返回值初始化引用;他是用
    []的返回值初始化引用
    在地图上。由于这会返回一个引用(而不是临时引用),没有寿命的延长。很好,我错过了。你能肯定地说它总是未定义的吗?@balki:不,尽管我没有多少疑问。据我所知,可以改进的范围来处理这个问题——因为标准中提供的转换是非规范性的,编译器知道我所知道的所有临时变量然而,我现在看到了Boost如何处理这个问题,因为类型系统没有足够的表达能力来反映这个问题。6.5.4/1中的转换是规范性的,除了只显示变量的名称
    \uu range
    \uu begin
    \uu end
    。我不相信编译器可以删除e此处未定义的行为不一定违反临时变量的生存期规则-因此这充其量只是一个不符合要求的扩展。似乎需要更改核心语言才能将
    中创建的任何临时变量的生存期延长到整个循环体。@Casey:啊,感谢您的澄清。是的,显然是必须对任何临时设备的使用寿命进行规范,否则将存在可移植性问题。
    auto&& m = foo();
    
    for (const int& i : m[42]) {
        std::cout << i << std::endl;
    }
    
    // from
    for (<init>: <expr>) {
        <body>
    }
    
    // to
    auto&& __container = <expr>;
    for (auto __it = begin(container), __e = end(container); __it != __e; ++__it)
    {
        <init> = *__it;
        <body>
    }
    
    auto&& __container = foo()[42];
    
    // non-standard gcc extension, very handy to model temporaries:
    std::vector<int>& __container = { std::map<...> m = foo(); m[42] };