当作为右值引用传递时,std :: bind会丢失引用

xis*_*xis 21 c++ stdbind

我有以下代码:

#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);
}
Run Code Online (Sandbox Code Playgroud)

它输出

1.000000
2.000000
2.000000
Run Code Online (Sandbox Code Playgroud)

这意味着该callback函数获得了副本t而不是引用t.我想知道发生了什么,因为std::bind它应该是完美的转发.

R. *_*des 36

std::bind默认情况下使用值语义.这是一个理智的默认设置,可让您安全地执行以下操作.

int f(double x);

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

使用值语义是安全的:绑定参数的生命周期成为bind返回的对象的生命周期.使用引用语义不具备这种保证.因此,当您需要引用语义时,您需要明确; 如果你遇到麻烦,那就是你的错.为此,您需要使用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);
}
Run Code Online (Sandbox Code Playgroud)

这个相同的协议在标准库的其他地方使用,就像std::thread构造函数一样.


And*_*owl 8

std::bind()是为价值语义而设计的(正如R. Martinho Fernandes在他的回答中很好地解释的那样),并内部创建副本.你需要/想要的是std::ref:

callback(std::bind(test, std::ref(t)));
//                       ^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

std::ref返回一个std::reference_wrapper<>包装对原始参数的引用的对象.这样,reference_wrapper周围的对象t被复制,而不是t自身.

这允许您在值语义(默认情况下假定)和引用语义(需要您的显式干预)之间进行选择.

这是一个实例.