【问题标题】:Default function that just returns the passed value?只返回传递值的默认函数?
【发布时间】:2013-02-18 14:17:05
【问题描述】:

作为一个懒惰的开发者,我喜欢用这个技巧来指定一个默认函数:

template <class Type, unsigned int Size, class Function = std::less<Type> >
void arrange(std::array<Type, Size> &x, Function&& f = Function())
{
    std::sort(std::begin(x), std::end(x), f);
}

但我在一个非常特殊的情况下遇到了问题,如下所示:

template <class Type, unsigned int Size, class Function = /*SOMETHING 1*/>
void index(std::array<Type, Size> &x, Function&& f = /*SOMETHING 2*/)
{
    for (unsigned int i = 0; i < Size; ++i) {
        x[i] = f(i);
    }
}

在这种情况下,我希望默认函数相当于:[](const unsigned int i){return i;}(一个只返回传递值的函数)。

为了做到这一点,我必须写什么来代替/*SOMETHING 1*//*SOMETHING 2*/

【问题讨论】:

    标签: c++ templates c++11 lambda std-function


    【解决方案1】:

    没有标准的函子可以做到这一点,但它很容易编写(尽管确切的形式存在争议):

    struct identity {
        template<typename U>
        constexpr auto operator()(U&& v) const noexcept
            -> decltype(std::forward<U>(v))
        {
            return std::forward<U>(v);
        }
    };
    

    可以这样使用:

    template <class Type, std::size_t Size, class Function = identity>
    void index(std::array<Type, Size> &x, Function&& f = Function())
    {
        for (unsigned int i = 0; i < Size; ++i) {
            x[i] = f(i);
        }
    }
    

    【讨论】:

    • +1,而是 Function() 而不是 identity() 作为默认参数。
    • 为什么在这里使用结构体?为什么不直接将identity 定义为一个函数?
    • @Claudiu:结构可以作为对象传递给元函数(这意味着可以进行模板参数的类型推导,也意味着内联对编译器来说更容易)。必须将裸函数作为函数指针传递。要将函数模板转换为函数指针,必须手动实例化模板(使用可能未知的类型参数)。
    • “很简单”。呵呵。
    【解决方案2】:

    这称为identity 函数。不幸的是,它不是 C++ 标准的一部分,但您可以轻松地自己构建一个。


    如果你碰巧使用g++,你可以用-std=gnu++11激活它的扩展,然后

    #include <array>
    #include <ext/functional>
    
    template <class Type, std::size_t Size, class Function = __gnu_cxx::identity<Type> >
    void index(std::array<Type, Size> &x, Function&& f = Function())
    {
        for (unsigned int i = 0; i < Size; ++i) {
            x[i] = f(i);
        }
    }
    

    也许它将在 C++20 中可用,请参阅std::identity。在此之前,您可以在 boost::compute::identity 上查看 boost 的版本。

    【讨论】:

    • 这里,默认参数可能也应该是Function()(即使Functionnot __gnu_cxx::identity&lt;Type&gt; 也允许它工作)。
    【解决方案3】:

    boost::phoenix 提供了一个完整的功能工具箱,这里的 'arg1' 是身份的 ident ;-)

    #include <boost/phoenix/core.hpp>
    
    template <class X, class Function = decltype(boost::phoenix::arg_names::arg1)>
    void index(X &x, Function f = Function()) {
        for (std::size_t i = 0; i < x.size(); ++i) {
                x[i] = f(i);
      }
    }
    

    【讨论】:

      【解决方案4】:

      您可以构建自己的身份函子:

      template <typename T>
      class returnIdentifyFunctor
      {
        public:
           auto operator ()(  T &&i ) -> decltype( std::forward<T>(i) )
          {
            return std::move(i);
          }
      };
      
      template <class Type, unsigned int Size, class Function = returnIdentifyFunctor<Type>>
      void index(std::array<Type, Size> &x, Function&& f = Function() )
       {
          for (unsigned int i = 0; i < Size; ++i) {
                  x[i] = f(i);
        }
      }
      

      【讨论】:

      • @DeadMG 谢谢你,很好的评论,我用我认为的最低要求编辑了我的答案,你能告诉我这是否解决了所有问题。我应该使用forward 而不是move
      【解决方案5】:

      boost 有以下变体:

      template <class Type, unsigned int Size, class Function = boost::function<Type(Type)>>
      void index(std::array<Type, Size> &x, Function&& f = boost::bind(std::plus<Type>(), 0, _1))
      

      【讨论】:

        【解决方案6】:

        解决这个问题的一种方法是拥有两个不同的功能。我发现使用默认参数是很明智的

        template <class Type, unsigned int Size, class Function>
        void index(std::array<Type, Size> &x, Function&& f){
            for(unsigned int i = 0; i < Size; ++i) x[i] = f(i);
        }
        
        template<class Type, unsigned int Size>
        void index(std::array<Type, Size> &x){
            return index(x, [](unsigned int i){return i;});                      // C++11 in this case
                        //, [](auto&& e){return std::forward<decltype(e)>(e)};); // C++14 in a more general case
                        //, std::identity); // C++20 in general
        }
        

        【讨论】:

        • C++14 版本很有意思。为什么我们要切换到更长且更难阅读的代码:D
        • @LightnessRacesinOrbit,这只是为了说明需要真实身份、通用、转发功能的更一般的上下文。
        • 所以换句话说就是过度设计了 ;)
        猜你喜欢
        • 1970-01-01
        • 2019-02-22
        • 2012-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-22
        相关资源
        最近更新 更多