【问题标题】:Create a wrapper for std algorithm functions to take container instead of iterators为 std 算法函数创建一个包装器以使用容器而不是迭代器
【发布时间】:2014-04-12 17:10:24
【问题描述】:

我想为带有 begin 和 end 迭代器的 std 函数创建包装器,以便它们也可以采用整个容器。例如,像

std::for_each(mycont, FUNC)

而不是

std::for_each(mycont.begin(), mycont.end(), FUNC)

我总体上了解分离算法和容器的好处,但我经常想对整个容器做一些事情,这种语法对我来说会更方便。如果你不同意,那也没关系。

我尝试了以下方法,但无法编译

namespace stx
{
    template<class CONT, class FUNC, class ...Ts>
    void blah(CONT& cont, FUNC func, Ts... args)
    {
        func(cont.begin(), cont.end(), args...);
    }
}

struct Bob
{
    void stuff()
    {
    }
};

std::vector<Bob> bobs(3);
stx::blah(bobs, &std::for_each, &Bob::stuff);

error C2783: 'void stx::blah(CONT &amp;,FUNC,Ts...)' : could not deduce template argument for 'FUNC'

我做错了什么? (VS2013)

【问题讨论】:

  • for_each 本身是一个模板函数,但编译器无法推断它的参数,因为它与它的用法没有关系(实际上你只是传递一个函数指针)。您必须明确指定模板参数或以某种方式关联它们(不仅仅是 FUNC,还有与 CONT 相关的东西)。
  • 此外,在函数的参数中包含函数指针通常是个坏主意。为了实现您想要做的事情并管理重载算法,您可能必须将它们包装在一些函子中。
  • 您可能需要考虑 Boost Range 库。它已经定义了采用“范围”参数的算法,并具有将容器视为范围的适配器。
  • 不幸的是,我想这样做(你可能尝试过)的方式是通过仿函数使用模板模板参数,但是模板模板参数必须是类(至少在我的工具链上,clang 3.4),not 函数,因此即使使用函子环绕,它也不能作为通用解决方案。如果我遇到了一种方法,我会回帖,但它看起来并不乐观。

标签: c++ c++11


【解决方案1】:

std::for_each 有一个模板签名:

template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

如果不指定 std::for_each 的模板类型,则不能将 std::for_each 作为函数参数传递。也就是说,你必须这样做:

stx::blah(bobs, &std::for_each<...>, &Bob::stuff);

将... 替换为std::for_each 的正确模板参数。

鉴于大多数std 函数都是模板化的,我认为你的想法是个坏主意。尽管我在大多数事情上都讨厌宏,但这是我能想到的逃离您正在创建的静态类型和模板地狱的唯一方法。

【讨论】:

    【解决方案2】:
    #define OVERLOAD_SET(F)\
      static struct { template<typename... Args> \
        auto operator()(Args&&...args) const { \
          return F(std::forward<Args>(args)); \
        } \
      }
    namespace stx {
      OVERLOAD_SET(std::foreach) for_each;
    }
    #undef OVERLOAD_SET
    

    现在stx::for_each 是一个单一对象,您可以将其传递给其他函数,以静态方式对std::for_each 进行重载查找。

    这可以做得更漂亮,甚至包括 ADL snd 隐式转换到函数指针。

    它确实需要为您希望以这种方式处理的每个模板函数创建一个重载集对象。哦,我使用了 C++1y 功能(返回类型推导改进),因为我很懒——如果您需要纯 C++11,请添加 decltype。

    【讨论】:

      【解决方案3】:

      这是一种按照您的要求进行操作的方法:

      #include <vector>
      #include <algorithm>
      #include <iostream>
      
      namespace stx 
      {
      template<class C, class F>
      F for_each(C const &c, F f) {
          return std::for_each(c.begin(), c.end(),f); 
      }
      }
      
      void printone(int a)
      {
          std::cout << ' ' << a;
      }
      
      int main()
      {
          std::vector<int> v(10);
          std::iota(v.begin(), v.end(), 1);
          stx::for_each(v, printone);
          std::cout << '\n';
          std::for_each(v.begin(), v.end(), printone);
          std::cout << '\n';
      }
      

      打印出来:

       1 2 3 4 5 6 7 8 9 10
       1 2 3 4 5 6 7 8 9 10
      

      这证明了std::for_each 和stx::for_each 的等价性。

      编辑: 请注意,我使用了 C++11-ism std::iota,但如果您使用的是较旧的编译器,则该行等效于

          for (int i=0; i < 10; ++i)
              v[i] = i+1;
      

      【讨论】:

      • 谢谢。这适用于for_each,但我试图避免为每个算法函数创建一个包装器。
      • 很公平,但我认为最终这将是最干净的解决方案。
      • 你应该通过引用来获取容器: template F for_each( C const& c, F f) { return std::for_each(c.begin(), c.结束(),f); }
      • @privatedatapublicchannel2 好点。我会更新答案,谢谢。
      【解决方案4】:

      要实现您的目标并管理重载算法,您必须忘记函数指针。

      与您尝试做的最接近的解决方案是:

      #include <algorithm>
      
      namespace stx {
          static struct {
              template <typename... Args>
              void operator()(Args&&... args) 
              {
                  std::for_each(std::forward<Args>(args)...);
              }
          }for_each;
      
          template<class Containter, typename Func, typename ...Args>
          void apply_on(Containter& c, Func func, Args... args)
          {
              func(c.begin(), c.end(), args...);
          }
      
          template<class Containter, typename... Args>
          auto for_each_on(Containter& c, Args... args)
              -> decltype(apply_on(c, for_each, args...))
          {
              return apply_on(c, for_each, args...);
          }
      }
      
      #include <functional> // C++11 or C++14 std::mem_fn
      #include <vector>
      #include <iostream>
      
      struct Bob {
          void stuff()
          {
              std::cout << "stuffing..." << std::endl;
          }
      };
      
      int main()
      {
          std::vector<Bob> bobs(3);
      
          // Now you can do :
          stx::apply_on(bobs, stx::for_each,  std::mem_fn(&Bob::stuff));
          // or
          stx::for_each_on(bobs, std::mem_fn(&Bob::stuff));
      
          return 0;
      }
      

      为每个算法提供一个包装器已经完成here。您还应该看看Boost.Range algorithms。这可能是您真正想要的。

      【讨论】:

        【解决方案5】:

        这个可以用

         template <typename containerT, typename FunctionT>
            FunctionT for_all(containerT cont,
                        FunctionT func)
            {
                typename containerT::iterator first = cont.begin();
                typename containerT::iterator last = cont.end();
                for (;first != last; ++first)
                    func(*first);
                return func;
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-30
          • 2019-06-06
          • 2023-03-05
          • 2018-05-20
          • 2012-04-26
          • 2012-08-03
          • 1970-01-01
          • 2013-11-08
          相关资源
          最近更新 更多