如何通过引用将函数绑定到对象

How to bind function to an object by reference?

本文关键字:绑定 对象 函数 何通过 引用      更新时间:2023-10-16

我有以下代码将成员函数绑定到类的实例:

class Foo {
public:
    int i;
    void test() {
        std::cout << i << std::endl;
    }
};
int main() {
    Foo f;
    f.i = 100;
    auto func = std::bind(&Foo::test, std::forward<Foo>(f));
    f.i = 1000;
    func();
}

但是std::bind语句不通过引用绑定到f。调用func会打印"100"而不是我想要的"1000"。

但是,如果我将语句更改为使用指针,它就会起作用。

auto func = std::bind(&Foo::test, &f);

但根据我的理解,这是通过指针传递f,正如我所认为的,std::bind采用r值引用Arg&&(如图所示),这怎么能工作呢?

有人能解释一下吗?

关于使用std::forward的注意事项

首先,std::forward用于完美转发,即转发参考类型(l值或r值)。

如果将l-value引用传递给std::forward,即返回的值,同样,如果传递r-valuereference,则返回r-value。这与总是返回r值引用的std::move相反。还要记住,命名的r值引用是l值引用。

/* Function that takes r-value reference. */
void func(my_type&& t) {
    // 't' is named and thus is an l-value reference here.
    // Pass 't' as lvalue reference.
    some_other_func(t);
    // Pass 't' as rvalue reference (forwards rvalueness).
    some_other_func(std::forward<my_type>(t));
    // 'std::move' should be used instead as we know 't' is always an rvalue.
    // e.g. some_other_func(std::move(t));
}

此外,对于以后需要访问某个状态的对象,永远不应该使用std::forwardstd::move。从中移动的对象被置于未指定但有效的状态,这基本上意味着除了销毁或重新分配状态外,您不能对它们执行任何操作。

通过引用传递给std::bind的参数

函数std::bind将始终复制或移动其参数。我在标准中找不到合适的引文,但en.cppreference.com说:

"要绑定的参数被复制或移动,除非包装在std::ref或std::cref中,否则从不通过引用传递。"

这意味着,如果将参数作为l值引用传递,则它是复制构造的,如果将其作为r值引用传递则它是移动构造的

为了避免这种情况,您可以使用std::ref作为可复制的引用包装器,它将在调用站点内部保留对变量的引用。

auto func = std::bind(&Foo::test, std::ref(f));

或者,您可以按照建议简单地将指针传递给f

auto func = std::bind(&Foo::test, &f);

然后发生的情况是,std::bind将对从调用f上的运算符地址返回的临时指针进行r值引用。此指针将被复制(因为指针无法移动)到从std::bind返回的绑定包装器对象中。即使指针本身被复制,它仍然会指向调用站点上的对象,并且您将实现所需的引用语义。

或者使用lambda,它通过引用捕获f并调用函数Foo::test。这是IMHO推荐的方法,因为在一般情况下,lambda比std::bind表达式更通用、更强大。

Foo f;
f.i = 100;
auto func = [&f] { f.test(); };
f.i = 1000;
func(); // 1000

注意:有关何时使用std::forward的详细说明,请参阅Scott Meyers的这段精彩视频。

std::bind采用的参数实际上是通用引用,可以绑定到左值和右值。您不能只将值传递给std::bind,因为这会复制它

要将引用传递到std::bind,可以使用std::ref:

auto func = std::bind(&Foo::test, std::ref(f));

现场演示