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

时间:2022-06-08 16:58:22

I have the following code to bind a member function to an instance of the class:

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

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();
}

But the std::bind statement does not bind to f by reference. Calling func prints "100" instead of "1000" which is what I want.

但是std::bind语句与f没有绑定。调用func打印“100”而不是“1000”,这是我想要的。

However, If I change the statement to take a pointer it works.

但是,如果我更改语句以获取它工作的指针。

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

But this is passing f by pointer by my understanding, and as I thought std::bind takes an r-value reference Arg&& (as shown here) how can this work?

但是这是通过我的理解传递f / by指针,并且正如我所认为的std::bind采用了一个r值引用Arg&& &&(如图所示)如何工作?

Can someone please explain this?

有人能解释一下吗?

2 个解决方案

#1


10  

Note on using std::forward

First of all, std::forward is meant to be used for perfect forwarding, i.e. to forward the reference type (l-value or r-value).

首先,std::forward是用来进行完美转发的,即转发引用类型(l-value或r-value)。

If you pass an l-value reference to std::forward that is what is returned, and likewise if an r-value reference is passed then an r-value is returned. This works as opposed to std::move that will always return an r-value reference. Also remember that named r-value references are l-value references.

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

/* 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));
}

Also you should never use std::forward or std::move on an object that you afterwards need to access some state from. Objects that are moved from are put in an unspecified but valid state, which basically means that you cant do anything with them except destroy or reassign a state to them.

此外,您不应该使用std: forward或std::move on an object, that you need to access some state from。被移动的对象被放在一个未指定但有效的状态中,这基本上意味着除了破坏或重新分配状态之外,您不能对它们做任何事情。

Arguments passed by reference to std::bind?

The function std::bind will always copy or move its arguments. I could not find an appropriate citation from the standard but en.cppreference.com says:

bind总是复制或移动它的参数。我无法从标准中找到合适的引用,但是en.cppreference.com说:

"The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref."

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

This means that if you pass the argument(s) as an l-value reference then it is copy constructed, and if you pass it as an r-value reference then it is move constructed. In either way the argument(s) will never be passed as references.

这意味着如果你把参数(s)作为l值引用传递,那么它就是复制构造的,如果你把它作为r值引用传递,那么它就是移动构造的。无论哪种方式,参数都不会作为引用传递。

To circumvent this you can e.g. use std::ref as a copyable reference wrapper that will internally keep a reference to the variable at the call site.

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

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

Or you could simply pass a pointer to f as you suggest.

或者你可以像你建议的那样通过一个指针到f。

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

What happens then is that std::bind will take an r-value reference to the temporary pointer returned from calling the address-of operator on f. This pointer will be copied (as pointers can't be moved) into the bound wrapper object returned from std::bind. Even though the pointer itself is copied, it will still point to the object at the call site and you will achieve the reference semantics you wanted.

然后发生的是,std::bind将对调用f上的address-of运算符返回的临时指针进行r-value引用。该指针将被复制(因为指针不能移动)到std:::bind返回的绑定包装器对象中。即使指针本身被复制,它仍然指向调用站点上的对象,您将实现所需的引用语义。

Alternatively use a lambda that captures f by reference and calls the function Foo::test. This is IMHO the recommended approach as lambdas are more versatile and powerful than std::bind expressions in the general case.

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

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

Note: for a good explanation of when to use std::forward see this excellent video by Scott Meyers.

注:关于何时使用std的一个很好的解释::向前看斯科特迈耶斯的精彩视频。

#2


4  

Arguments taken by std::bind are actually Universal references, which can bind to both lvalues and rvalues. You cannot just pass value to std::bind, since that will copy it.

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

To pass the reference to the std::bind you can use std::ref:

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

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

LIVE DEMO

现场演示

#1


10  

Note on using std::forward

First of all, std::forward is meant to be used for perfect forwarding, i.e. to forward the reference type (l-value or r-value).

首先,std::forward是用来进行完美转发的,即转发引用类型(l-value或r-value)。

If you pass an l-value reference to std::forward that is what is returned, and likewise if an r-value reference is passed then an r-value is returned. This works as opposed to std::move that will always return an r-value reference. Also remember that named r-value references are l-value references.

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

/* 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));
}

Also you should never use std::forward or std::move on an object that you afterwards need to access some state from. Objects that are moved from are put in an unspecified but valid state, which basically means that you cant do anything with them except destroy or reassign a state to them.

此外,您不应该使用std: forward或std::move on an object, that you need to access some state from。被移动的对象被放在一个未指定但有效的状态中,这基本上意味着除了破坏或重新分配状态之外,您不能对它们做任何事情。

Arguments passed by reference to std::bind?

The function std::bind will always copy or move its arguments. I could not find an appropriate citation from the standard but en.cppreference.com says:

bind总是复制或移动它的参数。我无法从标准中找到合适的引用,但是en.cppreference.com说:

"The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref."

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

This means that if you pass the argument(s) as an l-value reference then it is copy constructed, and if you pass it as an r-value reference then it is move constructed. In either way the argument(s) will never be passed as references.

这意味着如果你把参数(s)作为l值引用传递,那么它就是复制构造的,如果你把它作为r值引用传递,那么它就是移动构造的。无论哪种方式,参数都不会作为引用传递。

To circumvent this you can e.g. use std::ref as a copyable reference wrapper that will internally keep a reference to the variable at the call site.

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

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

Or you could simply pass a pointer to f as you suggest.

或者你可以像你建议的那样通过一个指针到f。

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

What happens then is that std::bind will take an r-value reference to the temporary pointer returned from calling the address-of operator on f. This pointer will be copied (as pointers can't be moved) into the bound wrapper object returned from std::bind. Even though the pointer itself is copied, it will still point to the object at the call site and you will achieve the reference semantics you wanted.

然后发生的是,std::bind将对调用f上的address-of运算符返回的临时指针进行r-value引用。该指针将被复制(因为指针不能移动)到std:::bind返回的绑定包装器对象中。即使指针本身被复制,它仍然指向调用站点上的对象,您将实现所需的引用语义。

Alternatively use a lambda that captures f by reference and calls the function Foo::test. This is IMHO the recommended approach as lambdas are more versatile and powerful than std::bind expressions in the general case.

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

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

Note: for a good explanation of when to use std::forward see this excellent video by Scott Meyers.

注:关于何时使用std的一个很好的解释::向前看斯科特迈耶斯的精彩视频。

#2


4  

Arguments taken by std::bind are actually Universal references, which can bind to both lvalues and rvalues. You cannot just pass value to std::bind, since that will copy it.

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

To pass the reference to the std::bind you can use std::ref:

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

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

LIVE DEMO

现场演示