【问题标题】:What is the right way to pass an object created by std::bind to a function?将 std::bind 创建的对象传递给函数的正确方法是什么?
【发布时间】:2020-05-14 08:49:44
【问题描述】:

假设我想通过引用函数来传递由std::bind 创建的函数对象:

void myCallback(int i, int j)
{
    std::cout << "toCall , i=" << i << " j=" << j;
}

void worker(std::function<void(int)> & callback)
{
    callback(111);
}

int main(int argc, char** argv)
{
    auto foo = std::bind(myCallback, std::placeholders::_1, 222);
    worker(foo);
}

这不会编译

严重性代码描述项目文件行抑制状态 错误 C2664 'void worker(std::function &)': 不能 将参数 1 从 'std::_Binder &,int>' 转换为 'std::function &' asn1test_1 D:.....\asn1test_1.....cpp 302

但是,按值传递有效:

void worker(std::function<void(int)> callback)
{
    callback(111);
}

当我避免使用“auto”并改用时

std::function<void(int)> foo = std::bind(myCallback, std::placeholders::_1, 222);

它既可以通过引用传递也可以通过值传递。

Q1:为什么会出现这种行为?

Q2:将std::bind 创建的对象传递给函数的“正确”方式或数据类型是什么?

【问题讨论】:

    标签: c++ pass-by-reference functor auto stdbind


    【解决方案1】:

    std::bind 不会返回 std::function,至少,它不需要这样做。当你这样做时

    auto foo = std::bind(myCallback, std::placeholders::_1, 222);
    worker(foo);
    

    foo 不是std::function 并且worker 需要对std::function 的非常量引用,因此编译器会出错。如果您将worker 切换为存在

    void worker(const std::function<void(int)> & callback)
    

    然后您将拥有一个 const 引用,它可以绑定到将 foo 转换为 std::function 会产生的临时对象。


    我还想指出,既然我们有 lambda,尤其是通用的,std::bind 真的不需要。保持对工人的改变,你可以改为foo

    auto foo = [](auto var){ return myCallback(var, 222); };
    

    【讨论】:

      【解决方案2】:

      std::bind() 不返回std::function,而是返回一个实现定义的类型,该类型可转换std::function

      worker() 将其callback 参数作为非常量引用时,这会阻止编译器执行任何隐式 转换,因为非常量引用不能绑定到临时对象.该引用将需要一个实际的 std::function 对象事先明确创建,例如:

      std::function<void(int)> foo = std::bind(myCallback, std::placeholders::_1, 222);
      worker(foo);
      

      worker() 更改为按值或通过常量引用获取其callback 参数,允许编译器为您执行隐式转换,因此您可以按原样传递std::bind() 的结果,编译器将为您创建一个临时std::function

      【讨论】:

      • 为什么编译器在有非常量引用时不能进行转换?
      • 即使它确实返回了一个 std::function,你也不能将对该(未命名的、临时的)值的引用作为非 const 左值引用传递。还涉及转换这一事实没有任何区别。
      • @michael 因为非常量引用不能绑定到临时对象。一个常量引用可以
      • @ChrisDodd 如果bind 确实返回了std::function,那么代码就可以工作。 OP 正在传递foo,这将是一个左值std::function
      • 我正在(错误地)阅读代码以直接将std::bind 作为参数传递给worker,而没有涉及auto foo。问题是否已编辑?
      【解决方案3】:

      如果您想避免转换为 std::function 的开销,您可以使用模板:

      template<class CB> void worker(CB callback) {
          callback(111);
      }
      

      【讨论】:

        【解决方案4】:

        应该是:

        void worker(std::function<void(int)> const& callback) {
            callback(111);
        }
        

        因为可以进行从foo 到临时std::function 对象的隐式转换。

        【讨论】:

        • 真正的问题是您不能将对未命名临时的引用作为非常量左值引用传递。
        • 我正在(错误地)阅读代码以直接将std::bind 作为参数传递给worker
        • 我明白了,这就是为什么我删除了对您的第一条评论的评论。 @ChrisDodd 如果你因为这个原因投了反对票,你现在可以删除反对票吗
        猜你喜欢
        • 1970-01-01
        • 2012-04-19
        • 2021-09-09
        • 2021-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-07
        • 1970-01-01
        相关资源
        最近更新 更多