21

我有以下代码:

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

它输出

1.000000
2.000000
2.000000

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

4

2 回答 2

37

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

int f(double x);

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

使用值语义是安全的:绑定参数的生命周期成为 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);
}

标准库的其他地方也使用了相同的协议,例如std::thread构造函数。

于 2013-07-02T17:34:18.417 回答
8

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

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

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

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

这是一个活生生的例子

于 2013-07-02T17:34:15.060 回答