就地构造 std::函数目标

Constructing std::function target in-place

本文关键字:函数 目标 std      更新时间:2023-10-16

我理解为std::function的典型用法

#include <iostream>
#include <functional>
using namespace std;
class C {
public: 
    C() { cout << "CREATING" << endl; }
    C(const C&) { cout << "COPY C "<< endl; };
    C(C&&) { cout << "MOVE C " << endl; };
    ~C() { cout << "DELETING"<< endl; }
    C& operator =(const C&) {
        cout << "COPY A " << endl; return *this;
    };
    C& operator =(C&&) {
        cout << "MOVE A" << endl; return *this;
    };
    void operator ()() const { cout << "CALLING" << endl; }
};
int main(int argc, char *argv[]) {
    function<void()> f = C();
    f();
    return 0;
}

输出后产量

CREATING
MOVE C
DELETING
CALLING
DELETING

显然,临时对象是在堆栈上创建的,然后移动到函数对象中。如果未提供移动构造函数,则改为复制它。
是否有一种标准方法可以在不需要临时对象的情况下设置目标?

§20.9.11.2.1 中的标准规定,从任何函子F f构造function的方式(假设f是非空的,强调我的):

*this以使用 std::move(f) 初始化的f的副本为目标

所以没有办法就地构造一个function。这让你在招致该移动之间做出选择:

function<void()> f1 = C{};

或者传入一些移动/复制成本更低的包装器C,无论是外部管理

C manage_me;
function<void()> f2 = std::ref(manage_me);

。或分配给operator()

function<void()> f3 = std::bind(&C::operator(), std::make_shared<C>());

如果operator()碰巧过载,最后一个可能会引起一些头痛,因为您必须将其转换为正确的类型,这是以前版本都不必处理的问题。

function<void()> f4 = std::bind(
                          static_cast<void(C::*)()>(&C::operator()),
                          std::make_shared<C>()
                          );
您可以使用

std::bind但实际上不需要为此实现operator()

int main(int argc, char *argv[]) {
    C c;
    function<void()> f = std::bind( &C::operator(), &c );
    f();
    return 0;
}

输出:

CREATING
CALLING
DELETING

当然,在这种情况下,您需要正确维护对象 C 的生存期。要使std::function拥有C实例并使其更加就地构造,可以使用std::shared_ptr

int main(int argc, char *argv[]) {
    function<void()> f = std::bind( &C::operator(), std::make_shared<C>() );
    f();
    return 0;
}