【发布时间】:2019-06-18 16:04:40
【问题描述】:
我有这个示例代码:
// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0
#include <functional>
#include <iostream>
#include <string>
void f(std::function<const std::string&()> fn) {
std::cout << "in f" << std::endl;
std::cout << "str: " << fn() << std::endl;
}
int main() {
std::string str = "a";
auto fn1 = [&]() { return str; };
auto fn2 = [&]() { const std::string& str2 = str; return str2; };
auto fn3 = [&]() -> const std::string& { return str; };
std::cout << "in main" << std::endl;
std::cout << "fn1: " << fn1() << std::endl;
std::cout << "fn2: " << fn2() << std::endl;
std::cout << "fn3: " << fn3() << std::endl;
f(fn1); // Segfaults
f(fn2); // Also segfaults
f(fn3); // Actually works
return 0;
}
当我第一次写这篇文章时,我预计在f() 中调用fn1() 会正确返回对main 中str 的引用。鉴于str 分配到f() 返回之后,这对我来说看起来不错。但实际发生的是试图在f() segfaults 中访问fn1() 的返回。
fn2() 也会发生同样的事情,但令人惊讶的是 fn3() 可以正常工作。
鉴于fn3() 有效而fn1() 无效,关于 C++ 如何推断 lambda 函数的返回值,我是否遗漏了什么?这将如何产生这个段错误?
作为记录,如果我运行此代码,以下是输出:
只打电话给f(fn3):
in main
fn1: a
fn2: a
fn3: a
in f
str: a
只打电话给f(fn2):
in main
fn1: a
fn2: a
fn3: a
in f
Segmentation fault (core dumped)
只打电话给f(fn1):
in main
fn1: a
fn2: a
fn3: a
in f
Segmentation fault (core dumped)
【问题讨论】:
-
前两个 lambda 按值返回,因此与
std::function不兼容,std::function期望通过引用返回的可调用对象。我不知道为什么会编译,因为我希望在尝试使用不兼容的可调用类型构造std::function时它会失败。 -
这是你在valgrind中运行的程序。
-
关于此类错误的一个建议:检查函数或 lambdas 返回的任何地址,以确保
f1()的结果与str相同,即assert(&str == &f1());。跨度> -
似乎 clang 不会中断:godbolt.org/z/PtJc3j
-
附带说明,
std::function<const T &()>接受T()函数并带有自动悬空引用的事实是一个错误的未记录功能。