【问题标题】:c++ std::bind universal reference can't compilec++ std::bind 通用引用无法编译
【发布时间】:2020-12-07 07:14:35
【问题描述】:
#include <functional>
#include <iostream>
#include <type_traits>

using cb_t = std::function<void ()>;

template<typename CB_T, std::enable_if_t<std::is_constructible<cb_t, CB_T>::value, bool> = true>
void
on_my_write(int a, int b, CB_T&& cb) {
    std::cout << "on_my_write:" << a << ", " << b << std::endl;
}
 

template<typename CB_T,  std::enable_if_t<std::is_constructible<cb_t, CB_T>::value, bool> = true>
void foo(CB_T&& cb) {
    auto b = std::bind(&on_my_write<CB_T>, std::placeholders::_1, std::placeholders::_2, std::forward<CB_T>(cb));
    b(1, 2);
}

int main() {
  
    foo([]{});

    return 0;
}

用命令行编译

g++ ./try1.cpp -std=c++17

给了我:

./try1.cpp: In instantiation of ‘void foo(CB_T&&) [with CB_T = main()::<lambda()>; typename >std::enable_if<std::is_constructible<std::function<void()>, CB_T>::value, bool>::type <anonymous> = 1]’:

./try1.cpp:39:13:   required from here
./try1.cpp:34:6: error: no match for call to ‘(std::_Bind<void (*(std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>))(int, int, main()::<lambda()>&&)>) (int, int)’
     b(1, 2);
     ~^~~~~~
In file included from ./try1.cpp:19:0:
/usr/include/c++/7/functional:547:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
  operator()(_Args&&... __args)
  ^~~~~~~~
/usr/include/c++/7/functional:547:2: note:   template argument deduction/substitution failed:
/usr/include/c++/7/functional:558:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
  operator()(_Args&&... __args) const
  ^~~~~~~~
/usr/include/c++/7/functional:558:2: note:   template argument deduction/substitution failed:
/usr/include/c++/7/functional:576:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
  operator()(_Args&&... __args) volatile
  ^~~~~~~~
/usr/include/c++/7/functional:576:2: note:   template argument deduction/substitution failed:
/usr/include/c++/7/functional:588:2: note: candidate: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int, int, main()::<lambda()>&&); _Bound_args = {std::_Placeholder<1>, std::_Placeholder<2>, main()::<lambda()>}]
  operator()(_Args&&... __args) const volatile
  ^~~~~~~~
/usr/include/c++/7/functional:588:2: note:   template argument deduction/substitution failed:

但是如果我把on_my_write(int a, int b, CB_T&amp;&amp; cb)改成on_my_write(int a, int b, const CB_T&amp; cb),那么它会编译运行成功。

我真的不明白为什么?

谁能给我解释一下?非常感谢!

我试了几个小时还是搞不定。

【问题讨论】:

  • 在现代 C++ 中(我看到你至少使用 C++17) lamdas 已经实际取代了 bind... 编译器通常甚至使用 lambdas 给出更好的结果

标签: c++ templates lambda


【解决方案1】:

是的,这有点棘手。 std::bind 当然不是神奇的,它必须以某种方式存储绑定的参数,并且默认将它们存储为值,除非使用 std::ref 参数请求。

意思是在调用std::bind(...,std::forward&lt;CB_T&gt;(cb))中,cb用于正确构造绑定成员变量,使用完美转发。

但是,遗憾的是,调用并没有通过完美转发完成,bind 将传递给on_my_write 的参数始终是一个左值。创建std::bind 版本来转发它并非不可能,我不知道为什么会这样,但一个很好的理由可能是这样的转发会使多次调用绑定函子变得危险。

因此,由于 std::bind 始终将参数作为左值传递,因此您有两种选择:

  1. 删除完美转发并像以前一样使用on_my_write(int a, int b, const CB_T&amp; cb)
  2. 删除完美转发并移动,使用on_my_write(int a, int b, CB_T&amp; cb) 并在里面使用std::move(cb) 来消费它。请注意,这正是应该只调用一次新函子的情况。
  3. 保持完美转发,但不要将其用于此调用,这可以通过手动添加引用来实现:
    std::bind(&on_my_write<CB_T&>, std::placeholders::_1, std::placeholders::_2,
           std::forward<CB_T>(cb));
    
    由于参考折叠规则,这将起作用,on_my_write(int a, int b, CB_T&amp; cb) 将始终被调用。您仍然可以使用 2. 并继续移动。
  4. 选择我忘记std::bind,使用lambda:
    template<typename CB_T,  std::enable_if_t<std::is_constructible<cb_t, CB_T>::value, 
    bool> = true>
    void foo(CB_T&& cb) {
        auto b = [&cb](auto a,auto b){return on_my_write(a,b,std::forward<CB_T>(cb));};
        b(1, 2);
    }
    
    这将正确转发cb,此解决方案的另一个优点是您不必手动推断on_my_write

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    相关资源
    最近更新 更多