接收具有不可复制成员变量和按值显式声明的析构函数的类的函数转换为 std::function 失败

LHL*_*ini 2 c++ stl

注意:这是这个类似问题的变体,但具有可移动对象和显式声明的析构函数。

下面的代码

#include <functional>
#include <memory>

struct S
{
    ~S();
    std::unique_ptr<int> x;
};

std::function<void(S)> x = [](S){};
Run Code Online (Sandbox Code Playgroud)

编译失败:

<source>:11:28: error: conversion from '<lambda(S)>' to non-scalar type 'std::function<void(S)>' requested
   11 | std::function<void(S)> x = [](S){};
      |
Run Code Online (Sandbox Code Playgroud)

如果我执行以下任一操作,它会按预期编译:

  1. 删除显式析构函数声明
  2. 删除x成员变量;
  3. 使用const S&而不是S.

为什么会这样?为什么明确声明很~S重要?

use*_*522 6

S不可复制,因为std::unique_ptr不可复制。

S也是不可移动的,因为析构函数的用户声明会阻止隐式声明移动操作。您必须手动声明(和默认/定义)它们。

一般来说,应该遵循五法则:如果声明析构函数,那么您还应该声明并默认/删除/定义所有复制/移动构造函数/赋值运算符。但是,只要功能上可能,请避免声明其中任何一个(零规则)。

然后std::function<void(S)>不能与 lambda 一起使用,因为它operator()需要std::function<void(S)>一个S作为参数,并且在转发到它时需要S从它自己的 lambda 参数进行移动构造Sstd::function<void(S)>因此,禁止从 lambda构造。

在 lambda 中使用const S&代替是S有效的,因为这样就没有必须移动构造的参数对象,并且使用右S值 from调用 lambdastd::function<void(S)>::operator()将是有效的。