Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::bind lose reference when delivered as rvalue reference

Tags:

c++

stdbind

I have the following code:

#include <stdio.h>
#include <functional>

template <typename T>
auto callback(T&& func) ->decltype(func())
{
    return func();
}

double test(double& value)
{
    value=value+1.0;
    return value;
}

int main(void)
{
    double t=1.0;
    printf("%f\n",t);
    test(t);
    printf("%f\n",t);
    callback(std::bind(test,t));
    printf("%f\n",t);
}

And it outputs

1.000000
2.000000
2.000000

Which implies the callback function got a copy of t instead of a reference to t. I am wondering what happened, since for std::bind it should be perfect-forwarding.

like image 418
xis Avatar asked Jul 02 '13 17:07

xis


2 Answers

std::bind uses value semantics by default. It's a sane default that lets you do things like the following safely.

int f(double x);

auto fun = std::bind(f, 1.0); // stores a copy, not a reference to a temporary
fun();

Using value semantics is safe: the lifetime of the bound arguments becomes the lifetime of the object returned by bind. Using reference semantics would not have that guarantee. So you are required to be explicit when you want reference semantics; if you get in trouble then it's your fault. In order to do that you need to use std::ref:

int main(void)
{
    double t=1.0;
    printf("%f\n",t);
    test(t);
    printf("%f\n",t);
    callback(std::bind(test, std::ref(t)));
    printf("%f\n",t);
}

This same protocol is used elsewhere in the standard library, like the std::thread constructor.

like image 79
R. Martinho Fernandes Avatar answered Nov 03 '22 23:11

R. Martinho Fernandes


std::bind() is designed for value semantics (as R. Martinho Fernandes nicely explains in his answer), and does create copies internally. What you need/want is std::ref:

callback(std::bind(test, std::ref(t)));
//                       ^^^^^^^^^^^

std::ref returns an std::reference_wrapper<> object that wraps a reference to your original argument. This way, the reference_wrapper object around t gets copied, and not t itself.

This allows you to choose between value semantics (assumed by default) and reference semantics (which requires your explicit intervention).

Here is a live example.

like image 25
Andy Prowl Avatar answered Nov 03 '22 23:11

Andy Prowl