什么时候应该存储函数的引用或指针?

When should I store reference or pointer to functions?

我有一个向量 std::functions

Using Functors = std::vector<std::function<bool(int)>>;
Functors fs;

现在,如果我想向向量添加一个函数,我会这样做:

fs.emplace_back(
    [](int v)->bool { return v > 1; }
)

我想知道当我声明 "Functors" 时,我是否应该使它指向 std::function 而不是原始的 std::function?这意味着,我将这样声明:

    Using Functors = std::vector<
     std::function<bool(int)>*
    >; // note the last &

因此当我调用emplace_back时,我只是传递一个指针而不是复制?或者 lambda 闭包是可移动的?请在这里说明一下:我什么时候应该使用对 std::function 的引用?

首先,引用不能是模板的参数类型,因此您需要使用指针或std::reference_wrapper代替:

std::vector<std::function<bool(int)>*>
std::vector<std::reference_wrapper<std::function<bool(int)>>>

在您的示例中,您的向量将无法保持 "references" 函数,因为您在 r 值上调用 emplace_back。所以在这里,std::vector<std::function<bool(int)>> 对我来说似乎是一个很好的解决方案。

只需使用 std::vector<std::function<bool(int)>>.

std::function搬家的时候几乎总是便宜的。

为了不便宜,存储的函数对象必须 (A) 很小,(B) 有一个 no-except 移动构造函数,并且 (C) 移动起来很昂贵。

有意这样一个对象并不难,但是不小心写一个对象就难了.

现在,标准没有强制要求多小;例如,对于 MSVC,他们将其设置为一或两个 std::string 的大小。

几乎可以肯定,与在此处使用值相比,管理原始指针的开销会使您的代码更糟且性能更低。一方面是因为修复原始指针错误会耗费脑力资源,另一方面是因为在自由存储上分配对象比 std::function 将几乎所有类型按数量级移动时所进行的分配成本更高。

平均而言,如果您使用 push_back 从空填充向量,则 std::vector 中的元素将移动固定次数(通常为 1-3 次),因为内存的要求托管呈指数增长,对象仅在需要调整大小时移动。

拥抱价值语义。

简而言之:永远不会!
(至少当你谈论 std::function<...>* 时不是 - 见下文)

如果你真的只想存储函数,做一个函数指针向量:

std::vector<bool(*)(int)> fs;

这也适用于在您的示例中使用空捕获的 lambda,因为它们隐式转换为函数指针。

如果您想存储任意有能力的对象,std::vector<std::function<bool(int)>>; 在 99.9% 的情况下应该没问题。很简单,你不太可能犯任何错误,也是最space有效的解决方案。

如果出于某种原因你真的想在堆上创建 std:: 函数对象,那么使用 unique_ptr:

std::vector<std::unique_ptr< std::function<bool(int)> >> fs;
fs.push_back( std::make_unique< std::function<bool(int)> >( [](int){ return true;}) );

这将确保您不会忘记删除这些对象。


解释:

也许我错了,但在我看来,您似乎误解了 std::function 是什么。它不是表示本机函数的 class,而是可以包装任何可调用对象(函数、函数对象、指向成员函数的指针 - 甚至指向成员的指针)的(相当重的)class。

  • 存储函数指针既非常高效(它们只有 4-8 个字节大,对 copy/move 来说微不足道)又安全,因为函数的地址在整个程序中都是有效的(忽略共享的动态加载)库)。
  • 通过 std::function 的间接访问增加了显着的开销(它们的大小通常是几个指针,而 move/copy 是实际函数)但它仍然是安全的。
  • 存储指向 std::function 的原始指针通常需要在堆上创建该对象,这会带来额外的开销,但至少移动又便宜了。然而,它伴随着原始拥有指针的常见问题。因此 std::unique_ptr 的解决方案。