捕获对对象的引用会导致分段错误
Capturing a reference to object causes segmentation fault
当通过引用或复制调用捕获引用时,对 name()
的调用会导致分段错误。
使用 std::bind
没有问题。为什么?
struct with_name
{
template<typename T>
explicit with_name(T& t)
: // name([&]{return t.name();})
name(std::bind(&T::name, &t))
{}
std::function<const std::string&()> name;
};
struct Person
{
const std::string& name() const
{
return name_;
}
std::string name_ = "Jon";
};
int main()
{
Person person{};
with_name withName{person};
// segmentation fault
std::cout << withName.name() << std::endl;
return 0;
}
来自 cppreference:
备注
当结果类型为引用的 std::function 是从没有尾随 return 类型的 lambda 表达式初始化时,应小心。由于自动推导的工作方式,这样的 lambda 表达式将始终 return 一个纯右值。因此,生成的引用通常会绑定到一个临时的,其生命周期在 std::function::operator() returns.
时结束
std::function<const int&()> F([]{ return 42; });
int x = F(); // Undefined behavior: the result of F() is a dangling reference
同样来自 cppreference:
template< class F >
function( F f );
- 用std::move(f)初始化目标。如果 f 是指向函数的空指针或指向成员的空指针,则 *this 在调用后将为空。此构造函数不参与重载决策,除非 f 对于参数类型 Args... 和 return 类型 R.
是 Callable
那么为什么当你在外部传递一个可调用对象时它不起作用,但是当你在参数范围内创建它时它是 UB?不是可以复制吗?
问题不是捕获引用,而是 lambda 的 return 类型与 std::function
的签名不匹配。给定 [&]{return t.name();}
,return 类型将是 std::string
,但 name
期望 returning const std::string&
.
将 lambda 更改为
template<typename T>
explicit with_name(T& t)
: name([&] () -> decltype(auto) {return t.name();})
// ^^^^^^^^^^^^^^
// deduce return type as const string& when `T` is Person
{}
当通过引用或复制调用捕获引用时,对 name()
的调用会导致分段错误。
使用 std::bind
没有问题。为什么?
struct with_name
{
template<typename T>
explicit with_name(T& t)
: // name([&]{return t.name();})
name(std::bind(&T::name, &t))
{}
std::function<const std::string&()> name;
};
struct Person
{
const std::string& name() const
{
return name_;
}
std::string name_ = "Jon";
};
int main()
{
Person person{};
with_name withName{person};
// segmentation fault
std::cout << withName.name() << std::endl;
return 0;
}
来自 cppreference:
备注 当结果类型为引用的 std::function 是从没有尾随 return 类型的 lambda 表达式初始化时,应小心。由于自动推导的工作方式,这样的 lambda 表达式将始终 return 一个纯右值。因此,生成的引用通常会绑定到一个临时的,其生命周期在 std::function::operator() returns.
时结束std::function<const int&()> F([]{ return 42; });
int x = F(); // Undefined behavior: the result of F() is a dangling reference
同样来自 cppreference:
template< class F >
function( F f );
- 用std::move(f)初始化目标。如果 f 是指向函数的空指针或指向成员的空指针,则 *this 在调用后将为空。此构造函数不参与重载决策,除非 f 对于参数类型 Args... 和 return 类型 R. 是 Callable
那么为什么当你在外部传递一个可调用对象时它不起作用,但是当你在参数范围内创建它时它是 UB?不是可以复制吗?
问题不是捕获引用,而是 lambda 的 return 类型与 std::function
的签名不匹配。给定 [&]{return t.name();}
,return 类型将是 std::string
,但 name
期望 returning const std::string&
.
将 lambda 更改为
template<typename T>
explicit with_name(T& t)
: name([&] () -> decltype(auto) {return t.name();})
// ^^^^^^^^^^^^^^
// deduce return type as const string& when `T` is Person
{}