c++11 - std::bind 和 boost::bind 技巧

标签 c++11 boost

#include <functional>
#include <boost/bind.hpp>
class A
{
public:
    A(){}
    ~A(){}
    template<typename _Handler>
    void call_handler(_Handler handler)
    {
        handler();
    }
};

class B
{
public:
    template<typename _Handler>
    void call_handler(_Handler handler)
    {

    }
    template<typename _Handler>
    void run(_Handler handler)
    {
        m_a.call_handler(boost::bind(&B::call_handler<_Handler>, this, handler));
        //only can use boost::bind here
    }
    A m_a;
};
class Test
{
public:
    void handler()
    {

    }
};

int main()
{
    B b;
    Test t;
    b.run(boost::bind(&Test::handler,&t));//only can use std::bind here
}

这是我上面的小测试代码。

我很困惑,我只能按特定顺序使用绑定(bind)...请参阅上面的注释

如果我将 std::bind 更改为 boost::bind,则编译器失败,反之亦然。

测试:

用于 cygwin 的 gcc 4.9.2,带有选项:

g++ -std=c++11 -fdiagnostics-color=always -fdiagnostics-show-location=every-line -I"/cygdrive/e/opensource libs/boost"main.cpp

msvc 12.0(带有 update4 的 visual studio 2013)具有默认选项。

gcc 诊断信息:

In file included from /cygdrive/e/opensource libs/boost/boost/bind.hpp:22:0, from main.cpp:3: /cygdrive/e/opensource libs/boost/boost/bind/bind.hpp: In instantiation of ‘void boost::_bi::list2::operator()(boost::_bi::type, F&, A&, int) [with F = boost::_mfi::mf1, boost::_bi::list1 > > >; A = boost::_bi::list0; A1 = boost::_bi::value; A2 = boost::_bi::bind_t, boost::_bi::list1 > >]’: /cygdrive/e/opensource libs/boost/boost/bind/bind.hpp:893:50:
required from ‘boost::_bi::bind_t::result_type boost::_bi::bind_t::operator()() [with R = void; F = boost::_mfi::mf1, boost::_bi::list1 > > >; L = boost::_bi::list2, boost::_bi::bind_t, boost::_bi::list1 > > >; boost::_bi::bind_t::result_type = void]’ main.cpp:12:11:
required from ‘void A::call_handler(_Handler) [with _Handler = boost::_bi::bind_t, boost::_bi::list1 > > >, boost::_bi::list2, boost::_bi::bind_t, boost::_bi::list1 > > > >]’ main.cpp:27:3:
required from ‘void B::run(_Handler) [with _Handler = boost::_bi::bind_t, boost::_bi::list1 > >]’ main.cpp:44:38:
required from here /cygdrive/e/opensource libs/boost/boost/bind/bind.hpp:313:34: error: invalid use of void expression unwrapper::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); ^

msvc 12.0 诊断消息:

1>------ Build started: Project: scince_x32, Configuration: Debug Win32 ------ 1> main.cpp 1>e:\opensource libs\boost\boost\bind\bind.hpp(313): error C2664: 'void boost::_mfi::mf1::operator ()(T *,A1) const' : cannot convert argument 2 from 'void' to 'boost::_bi::bind_t,boost::_bi::list1>>' 1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B 1> , A1=boost::_bi::bind_t,boost::_bi::list1>> 1> ] 1> and 1> [ 1> T=Test * 1> ] 1> Expressions of type void cannot be converted to other types 1> e:\opensource libs\boost\boost\bind\bind.hpp(893) : see reference to function template instantiation 'void boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>::operator ()(boost::_bi::type,F &,A &,int)' being compiled 1> with 1> [ 1> T=B * 1> ,
F=boost::_mfi::mf1,boost::_bi::list1>>> 1> , A=boost::_bi::list0 1> ] 1> e:\opensource libs\boost\boost\bind\bind.hpp(893) : see reference to function template instantiation 'void boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>::operator ()(boost::_bi::type,F &,A &,int)' being compiled 1> with 1> [ 1> T=B * 1> ,
F=boost::_mfi::mf1,boost::_bi::list1>>> 1> , A=boost::_bi::list0 1> ] 1> e:\opensource libs\boost\boost\bind\bind.hpp(891) : while compiling class template member function 'void boost::_bi::bind_t,boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>>::operator ()(void)' 1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B * 1> ] 1> e:\c++program\scince_x32\scince_x32\main.cpp(12) : see reference to function template instantiation 'void boost::_bi::bind_t,boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>>::operator ()(void)' being compiled 1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B * 1> ] 1> e:\c++program\scince_x32\scince_x32\main.cpp(27) : see reference to class template instantiation 'boost::_bi::bind_t,boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>>' being compiled 1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B * 1> ] 1> e:\c++program\scince_x32\scince_x32\main.cpp(44) : see reference to function template instantiation 'void B::run,boost::_bi::list1>>>(_Handler)' being compiled 1> with 1> [ 1> T=Test * 1> ,
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> ] ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

最佳答案

您正在(间接地)绑定(bind)一个绑定(bind)表达式,该绑定(bind)表达式作为参数绑定(bind)另一个绑定(bind)表达式。

在 Boost 和标准库中,您需要保护内部绑定(bind)表达式,以便占位符/绑定(bind)不会混合和冲突。

所以它会以

开始
m_a.call_handler(boost::bind(&B::call_handler<_Handler>, this, boost::protect(handler)));

除了 protect(handler)包装类型,它不再是 _Handler , 所以你需要其他东西来表示 &B::call_handler<???> .根据我的经验,到目前为止最简单的方法是使用多态函数对象:

struct handler_caller_f {
    typedef void result_type;

    template <typename H> void operator()(B* /*this_*/, H/* handler*/) const {
        std::cout << __PRETTY_FUNCTION__ << "\n"; 
    }
};

template <typename H> void run(H handler) {
    m_a.call_handler(boost::bind(handler_caller_f(), this, boost::protect(handler)););
}

如您所见,函数对象替换了 B::call_handler并通过再次推断处理程序的类型来解决问题。

这是一个清理后的版本:

使用 boost

Live On Coliru

#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <iostream>

struct A {
    template <typename H> void call_handler(H handler) { 
        handler(); 
    }
};

struct B {
    struct handler_caller_f {
        typedef void result_type;

        template <typename H> void operator()(B* this_, H handler) const {
            handler();
        }
    };

    template <typename H> void run(H handler) {
        m_a.call_handler(boost::bind(handler_caller_f(), this, boost::protect(handler)));
    }

    A m_a;
};

struct Test {
    void handler() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};

int main() {
    Test t;

    B b;
    b.run(boost::bind(&Test::handler, &t));
}

打印

void Test::handler()

使用std::bind

标准库没有protect , 但很容易添加:

Live On Coliru

#include <functional>
#include <iostream>

namespace std_ex { // http://stackoverflow.com/questions/18519087/why-is-there-no-stdprotect
template <typename T> struct protect_wrapper : T {
    protect_wrapper(const T &t) : T(t) {}
    protect_wrapper(T &&t) : T(std::move(t)) {}
};

template <typename T>
typename std::enable_if<!std::is_bind_expression<typename std::decay<T>::type>::value, T && >::type protect(T &&t) {
    return std::forward<T>(t);
}

template <typename T>
typename std::enable_if<std::is_bind_expression<typename std::decay<T>::type>::value,
                        protect_wrapper<typename std::decay<T>::type> >::type
protect(T &&t) {
    return protect_wrapper<typename std::decay<T>::type>(std::forward<T>(t));
}
}

struct A {
    template <typename H> void call_handler(H handler) { handler(); }
};

struct B {
    struct handler_caller_f {
        typedef void result_type;

        template <typename H> void operator()(B *this_, H handler) const { handler(); }
    };

    template <typename H> void run(H handler) {
        m_a.call_handler(std::bind(handler_caller_f(), this, std_ex::protect(handler)));
    }

    A m_a;
};

struct Test {
    void handler() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};

int main() {
    Test t;

    B b;
    b.run(std::bind(&Test::handler, &t));
}

关于c++11 - std::bind 和 boost::bind 技巧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30598090/

相关文章:

c++ - 多类型STL映射

c++ - 在新的 C++11 中可以创建可选引用吗?

c++ - 为什么 Boost Log 记录器操作不是常量?

c++ - boost::序列化和工厂模式设计

c++ - 为什么我们可以非常量引用一个临时对象并延长它的生命周期?

c++ - map 和unordered_map

c++ - 带有参数列表的父类(super class)方法 <superclass> 与专用列表一起使用

c++ - 从 boost::asio::streambuf 到 std::string 的高效数据复制

c++ - 类似 Parse 的 CSV(空白分隔符和 boost )

c++ - boost::interprocess - allocate_aligned - 在所有进程中保证相同的对齐方式?