【问题标题】:Rationale behind making std::function require copy constructor使 std::function 需要复制构造函数的基本原理
【发布时间】:2021-06-01 04:51:51
【问题描述】:

我最近尝试做这样的事情:

auto x = std::make_unique<int>(1);
auto l = [y = std::move(x)]() { return *y; };
std::function<void()> f(std::move(l)); //error, requires copy construction

令我非常失望和困惑的是,它向我抛出了一堆错误消息。如您所知,std::function 不允许从不可复制构造的类型构造。有什么具体原因吗?还是标准中的一个忽略?仅移动类型的构造会带来什么问题?

【问题讨论】:

  • 当您复制std::function 时会发生什么?
  • std::function 必须是可复制的,否则您将失去该类型的巨大优势。并且有条件地删除复制构造函数(从而启用仅移动对象)不起作用,因为std::function 依赖于类型擦除。只有std::function 的构造函数会知道一个类型是否可复制。但是该信息已经在类范围内丢失了。

标签: c++ c++14


【解决方案1】:

std::function 可以采用只能移动的可调用对象,但只能通过将自身限制为始终只能移动。它使用类型擦除来存储对象,因此它的静态行为必须代表它所存储的对象所需的最低公分母功能。

事情是这样的:在 C++ 及其标准库中有很多地方希望可调用对象是可复制的。 “很多”是指整个标准算法库。甚至 C++20 概念indirect_unary_predicate requires copy_constructible。本质上,所有算法都可以随意复制给定函数;他们甚至通过价值来获取这些功能。

只能移动的std::function 类型永远不能与任何此类算法一起使用。而只移动是std::function 采用只移动类型的唯一方法。

【讨论】:

  • 但是为什么 OP 的代码需要复制可构造性呢?我可以理解,如果调用std::function 的复制构造函数,它将无法编译。但是,std::function 的构造函数应该是initialize it's "copy" of constructor argument with std::move
  • @DanielLangr:这是我的第二句话:“它使用类型擦除来存储对象,因此它的静态行为必须代表它所存储的对象所需的最低公分母功能。”如果它需要的类型的最小公分母是“只移动”,那么std::function 本身必须是只移动的。这就是类型擦除的工作原理。
  • 感谢您的澄清。我创建了一个简化的function,其中the problem is simulated。我首先认为Derived&lt;T&gt;::clone 不需要编译,当时function 的复制构造函数在任何地方都没有涉及。但是由于clonevirtual,所以即使不使用它似乎也必须编译。这个解释对吗? (它有点类似于this demo,其中f 未编译但g 即使未使用,因为它是virtual)。
  • @DanielLangr 这就是类型擦除的重点。当我们退出已擦除容器的构造函数(在您的情况下为function)时,我们会丢失有关类型是可复制还是可移动构造的信息。那时,我们必须遵守已擦除容器指定的合约,因为无法找回丢失的信息(至少在今天)。继承的使用只是一个实现细节,并不真正相关。是的,必须实例化虚函数,因为您可以通过基指针访问它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多