24

为什么在C++11std::protect中不能与 with 一起使用?std::bind

Boost.Bind提供了一个boost::protect帮助器来包装它的参数,这样boost::bind就不会识别和评估它。std::[c]ref大多数情况下将是一个足够好的替代品,除了它不会将右值作为参数。

举一个具体的例子,考虑以下人工情况:

#include <type_traits>
#include <functional>

int add(int a, int b)
{ return a + b; }

struct invoke_with_42
{
    template <typename FunObj>
    auto operator()(FunObj&& fun_obj) const -> decltype((fun_obj(42)))
    { return fun_obj(42); }
};

int main()
{
    //// Nested bind expression evaluated
    //auto bind_expr =
    //    std::bind<int>(invoke_with_42{}
    //      , std::bind(&add, 1, std::placeholders::_1));

    //// Compilation error, cref does not take rvalues
    //auto bind_expr =
    //    std::bind<int>(invoke_with_42{}
    //      , std::cref(std::bind(&add, 1, std::placeholders::_1)));

    //// Ok, inner_bind_expr must be kept alive
    auto inner_bind_expr =
        std::bind(&add, 1, std::placeholders::_1);
    auto outer_bind_expr =
        std::bind<int>(invoke_with_42{}, std::cref(inner_bind_expr));


    //// Ok, with protect
    //auto bind_expr =
    //    std::bind<int>(invoke_with_42{}
    //      , std::protect(std::bind(&add, 1, std::placeholders::_1)));
}
4

1 回答 1

15

好吧,我不知道为什么没有实施。也许它没有被提议,或者也许有一些微妙的陷阱。

也就是说,我认为你可以很容易地写出来

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

保护的两个版本使得非绑定表达式不被包装(它们只是通过)。其他所有内容都通过移动/复制传递给protect_wrapper,它只是从类型继承。这允许类型的函数通过,或将其转换为类型。

但是,它会进行复制/移动,因此可以安全地与 rvals 一起使用。而且由于它只保护属于 bind_expressions 的类型,因此可以最大限度地减少必须发生的复制量。

int main()
{

    //// Ok, with protect
    auto bind_expr =
        std::bind<int>(invoke_with_42{}
          , protect(std::bind(&add, 1, std::placeholders::_1)));


    std:: cout << bind_expr() << std::endl;
    return 0;

}
于 2013-08-29T20:08:08.173 回答