Подтвердить что ты не робот

Почему нет std:: protect?

Почему нет std::protect для использования вместе с std::bind в С++ 11?

Boost.Bind предоставляет помощник boost::protect, который переносит свой аргумент, чтобы boost::bind не распознавал и не оценивал его. std::[c]ref будет достаточно хорошей заменой в большинстве случаев, за исключением того, что он не будет принимать значение rvalue в качестве аргумента.

Для конкретного примера рассмотрим следующую искусственную ситуацию:

#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)));
}
4b9b3361

Ответ 1

Ну, я не знаю, почему это не было реализовано. Возможно, это не было предложено, или, возможно, были некоторые тонкие ошибки.

Тем не менее, я думаю, вы можете легко записать его

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;

}