Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
322 views
in Technique[技术] by (71.8m points)

c++ - Why is there no std::protect?

Why is there no std::protect to use together with std::bind in C++11?

Boost.Bind provides a boost::protect helper that wraps its argument so that boost::bind does not recognize and evaluate it. std::[c]ref would be a good enough replacement most of the times, except that it won't take an rvalue as argument.

For a concrete example, consider the following artificial situation:

#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)));
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Well, I'm not aware of why it wasn't implemented. Perhaps it wasn't proposed, or perhaps there were some subtle gotchas.

That said, I think you can write it pretty easily

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

The two versions of protect are so that non-bind expressions are not wrapped (they just pass through). Everything else is passed by move/copy to the protect_wrapper, which simply inherits from the type. This allows the type's functions to pass through, or for it to convert to the type.

It makes a copy/move however, so it can be safely used with rvals. And since it only protects types that are bind_expressions, it minimizes the amount of copying that has to occur.

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;

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...