19

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

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&&(如此处所示)如何工作?

有人可以解释一下吗?

4

2 回答 2

29

使用注意事项std::forward

首先,std::forward意味着用于完美转发,即转发引用类型(左值或右值)。

如果您传递一个左值引用std::forward,则返回该值,同样,如果传递一个右引用,则返回一个右值。这与std::move始终返回 r 值引用相反。还要记住命名的右值引用是左值引用。

/* 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 中。”

这意味着如果您将参数作为左值引用传递,那么它是复制构造的,如果您将它作为右值引用传递,那么它是移动构造的。无论哪种方式,参数都永远不会作为引用传递。

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

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

或者您可以f按照您的建议简单地传递一个指针。

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

然后std::bind会发生 r-value 引用到从调用 address-of 运算符返回的临时指针 on f。该指针将被复制(因为指针无法移动)到从std::bind. 即使指针本身被复制,它仍将指向调用站点的对象,您将获得所需的引用语义。

或者使用通过引用捕获并调用函数的lambda。恕我直言,这是推荐的方法,因为 lambda在一般情况下比表达式更加通用和强大。fFoo::teststd::bind

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

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

于 2014-10-03T23:44:20.293 回答
6

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

要将引用传递给std::bind您可以使用std::ref

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

LIVE DEMO

于 2014-10-03T21:42:18.793 回答