含不可拷贝成员与显式析构函数的类传值时,无法转换为std::function?
显式声明析构函数导致lambda无法转换为std::function的原因分析
问题背景
这是涉及可移动对象与显式声明析构函数的C++问题变体。
编译失败的代码
#include <functional> #include <memory> struct S { ~S(); std::unique_ptr<int> x; }; std::function<void(S)> x = [](S){};
编译错误信息
<source>:11:28: error: conversion from '<lambda(S)>' to non-scalar type 'std::function<void(S)>' requested 11 | std::function<void(S)> x = [](S){}; |
可修复编译的三种操作
- 移除显式析构函数声明;
- 移除成员变量
x; - 将lambda的参数改为
const S&而非S。
核心原因
显式声明析构函数的关键影响
当显式声明类的析构函数~S()时,C++编译器会自动抑制默认移动构造函数的生成。而S中包含的std::unique_ptr<int>本身是不可拷贝的类型,因此S的拷贝构造函数会被编译器自动删除。
此时S处于既不可拷贝,也不可移动的状态。
为什么会导致std::function转换失败
std::function<void(S)>要求其包装的可调用对象(这里是lambda)能够接收S类型的参数。lambda的参数是按值传递的S,这意味着每次调用lambda时,都需要构造S的实例(从传入参数拷贝或移动而来)。但因为S既不可拷贝也不可移动,这个构造操作无法完成,lambda也就无法满足std::function对可调用性的要求,最终导致转换失败。
三种修复方法的原理
- 移除显式析构函数:此时编译器会自动生成默认的移动构造函数(因为
S包含不可拷贝成员,编译器不会生成拷贝构造,但会生成移动构造),S变为可移动类型,lambda的按值参数可以通过移动构造完成传递,std::function的构造也就正常了。 - 移除成员变量
x:此时S没有不可拷贝的成员,即使显式声明了析构函数,编译器会自动生成拷贝构造函数,S变为可拷贝类型,lambda的按值参数可以正常传递。 - 将参数改为
const S&:此时lambda接收的是S的常量引用,不需要构造S的副本,完全避开了对S拷贝或移动构造的需求,自然不会触发错误。
内容的提问来源于stack exchange,提问作者LHLaurini
相关产品推荐
相关产品推荐

