【问题标题】:lambda: should capturing const reference by reference yield undefined behaviour?lambda:应该通过引用捕获 const 引用产生未定义的行为吗?
【发布时间】:2012-06-10 14:24:54
【问题描述】:

我刚刚在我的代码中发现了一个令人讨厌的错误,因为我通过引用捕获了对字符串的 const 引用。运行 lambda 时,原始字符串对象早已不复存在,并且引用的值是空的,而目的是它包含原始字符串的值,因此存在错误。

让我感到困惑的是,这并没有在运行时引发崩溃:毕竟,这不应该是未定义的行为,因为 afaik 有一个悬空引用吗?此外,在调试器下查看 id 时,它甚至看起来不像垃圾,而只是一个正确构造的空字符串。

这是测试用例;这只是打印一个空行:

typedef std::vector< std::function< void() > > functions;

void AddFunction( const std::string& id, functions& funs )
{
  funs.push_back( [&id] ()
    {
        //the type of id is const std::string&, but there
        //is no object to reference. UB?
      std::cout << id << std::endl;
    } );
}

int main()
{
  functions funs;
  AddFunction( "id", funs );
  funs[ 0 ]();
}

【问题讨论】:

  • 你是临时到 const-reference 绑定的又一个受害者 :(
  • 是的,我已经知道了,幸运的是单元测试指出了这一点
  • 您可能没有那么幸运,而且它可能一直在正常工作。想象一下编译器在调用AddFunction 之后调整了堆栈,但是临时驻留的堆栈区域仍然完好无损。然后有一天,kaboom!
  • 这带来了一个问题,你可以指定lambda来捕获引用或复制,但是没有办法告诉它从临时移动。
  • @Gene 确实很有趣的问题..

标签: c++ lambda c++11 undefined-behavior


【解决方案1】:

未定义的行为意味着不需要发生什么。没有要求它应该崩溃。无论你的悬空引用指向什么内存,它都没有理由不应该包含看起来像空字符串的东西,string 的析构函数将内存留在那个状态是合理的。 p>

【讨论】:

  • @stijn:不能保证,但是如果析构函数确实让内存看起来像一个空字符串,那可能不是偶然的,因为它清除了一些字段。侥幸的部分是,用于包含临时字符串的堆栈区域尚未被重用。
  • 只是为了测试,我在一个实际的应用程序中添加了上面的代码,在那里我确实得到了运行时崩溃,因为你说的确切原因:一旦内存被重用,字符串就会变成假的
【解决方案2】:

通过引用捕获任何东西意味着必须注意它是否存在足够长的时间。如果您不这样做,该程序可能会正常工作,但它可能只是调用 Domino's 并订购双份意大利辣香肠。至少,按照标准。

【讨论】:

  • 唉,还没有人向我展示过一个可以实际上在 UB 上订购披萨的编译器,所以我开始对这个例子保持警惕。
  • 我宁愿向 GCC 人员发送一个带有一些 IP 地理位置的补丁 :-)
  • @Kerrek SB:你需要在代码中引入更多的 UB 来提高几率:)
【解决方案3】:

(正如 dascandy 所指出的)这个问题与 const 和引用语法几乎没有关系,更简单地说,它放弃了确保在任何时候通过引用传递的所有内容都存在的责任。 .函数调用中的文字对于该调用来说是严格临时的,并在返回时消失,所以我们正在访问一个临时的 - 编译器经常检测到的缺陷 - 只是在这种情况下不是。

typedef std::vector<std::function<void()> > functions;

void AddFunction(const std::string& id, functions& funs) {
    funs.push_back([&id] ()
    {
        //the type of id is const std::string&, but there
        //is no object to reference. UB?
            std::cout <<"id="<< id << std::endl;
        });
}

int emain() {
    functions funs;

    std::string ida("idA");
           // let idB be done by the tenporary literal below
    std::string idc("idC");

    AddFunction(ida, funs);
    AddFunction("idB", funs);
    AddFunction(idc, funs);
    funs[0]();
    //funs[1](); // uncomment this for (possibly) bizarre results   
    funs[2]();
    std::cout<<"emain exit"<<std::endl;
    return 0;
}

int main(int argc, char* argv[]){
    int iret = emain();
    return 0;
}

【讨论】:

    猜你喜欢
    • 2018-11-19
    • 2015-09-25
    • 2019-08-28
    • 1970-01-01
    • 2015-11-02
    • 2016-02-08
    • 1970-01-01
    相关资源
    最近更新 更多