【问题标题】:Why is there no std::protect?为什么没有std::protect?
【发布时间】:2013-08-29 19:02:33
【问题描述】:

为什么在C++11中没有std::protectstd::bind一起使用?

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

【问题讨论】:

  • 有人提出来了吗?
  • cref 上的 rvalue 可能会是灾难性的——只要将它传递给的 bind 对象(或其他),临时生命周期几乎不会保留它。
  • 您也可以通过将bind 结果分配给std::function 来“保护”,尽管这会增加运行时开销。
  • C++11 &lt;functional&gt; 是从 TR1 库演变而来的,TR1 库是一个独立于 2006 年左右的 C++ 的 ISO 标准,它从 Boost 中采用。所以你可能会看到一些不同的演变。
  • 也许更好的名字是std::hold(如reference.wolfram.com/language/ref/Hold.html)。在我看来,如果有一个叫做std::protect 的东西对我来说意味着一个明确的cast-to-const。并不是说我总是为此编写一个也称为protect 的函数:)。换句话说,我想知道protect 一开始是不是一个坏名字。

标签: c++ c++11


【解决方案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 的两个版本使得非绑定表达式不被包装(它们只是通过)。其他所有内容都通过移动/复制传递给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;

}

【讨论】:

  • 有了C++11继承构造函数,整个类定义就只是template&lt;typename T&gt; struct protect_wrapper : T { using T::T; };
  • 在我看来 boost::protect 的目的是允许人们编写无法维护的代码...如果 lambda 表达式这么复杂,是时候说重构它?
  • @RichardHodges:虽然很少见,但我认为boost::protect 的目的是允许您编写一个将函数作为参数的函数,而不会绊倒bind 通常如何处理嵌套绑定.虽然在 C++ 中一个函数接受另一个函数并不常见,但有更多的函数式编程风格发现这很正常。
  • 是的,我曾在一个使用 lambda 的 lambda 的多资产定价系统上工作过......“世界上最容易混淆的程序”奖的优秀候选人......
猜你喜欢
  • 2017-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-09
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多