24

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)));
}
K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • 10
    Was it proposed? – Benjamin Lindley Aug 29 '13 at 19:12
  • 1
    A `cref` on an `rvalue` would probably be disastrous -- the temporaries lifetime would have little keeping it around as long as the `bind` object it is being passed to (or whatever). – Yakk - Adam Nevraumont Aug 29 '13 at 19:39
  • You can also "protect" by assigning the `bind` result to a `std::function`, although it adds runtime overhead. – Potatoswatter Aug 30 '13 at 13:58
  • 1
    C++11 `` is evolved from the TR1 library, an ISO standard separate from C++ circa 2006, which adopted it from Boost. So you're probably seeing some divergent evolution. – Potatoswatter Aug 31 '13 at 07:31
  • Maybe a better name for that could have been `std::hold` (like http://reference.wolfram.com/language/ref/Hold.html). In my opinion if there is something called `std::protect` it implies to me an explict `cast-to-const`. Not that I always write a function also called `protect` for that :) . In other words, I wonder if `protect` was a bad name to begin with. – alfC Jan 21 '15 at 03:21

1 Answers1

15

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;

}
Dave S
  • 20,507
  • 3
  • 48
  • 68
  • 10
    With C++11 inheriting constructors, the entire class definition is just `template struct protect_wrapper : T { using T::T; };` – Potatoswatter Aug 31 '13 at 07:28
  • It seems to me that the purpose of boost::protect is to allow people to write unmaintainable code... Wouldn't it be fair to say that if a lambda expression is this complicated it's time to refactor it? – Richard Hodges Dec 17 '14 at 18:41
  • @RichardHodges: While it's rare, I think the purpose of `boost::protect` is to allow you to write a function which takes a function as an argument, without tripping over how `bind` usually handles nested binds. While having one function that takes another isn't common in C++, there are more functional programing styles that find that normal. – Dave S Dec 17 '14 at 22:40
  • Yes I've worked on a multi-asset pricing system that used lambdas of lambdas... An excellent candidate for the "most obfuscated program in the world" award... – Richard Hodges Dec 17 '14 at 22:43