【问题标题】:simple C++ templates suited for STL Containers适用于 STL 容器的简单 C++ 模板
【发布时间】:2009-01-02 14:25:25
【问题描述】:

我需要一个这样的模板,效果很好

template <typename container> void mySuperTempalte (const container myCont)
{
    //do something here
}

然后我想将上面的模板专门用于 std::string 所以我想出了

template <typename container> void mySuperTempalte (const container<std::string> myCont)
{
    //check type of container
    //do something here
}

这不起作用,并引发错误。我想让第二个示例工作,然后如果可能的话,我想在模板中添加一些代码来检查是否使用了 std::vector/std::deque/std::list ,以便在每个示例中执行不同的操作案子。 所以我使用了模板,因为 99% 的代码对于向量和双端队列等都是相同的。

【问题讨论】:

  • 是否要专门针对不同的内容或容器不太清楚。您是否将 std::string 视为未知容器的存储类型?还是您正在考虑将 std::string 作为 char 的容器?

标签: c++ templates stl containers


【解决方案1】:

专业化:

template<> void mySuperTempalte<std:string>(const std::string myCont)
{
    //check type of container
    //do something here
}

专门用于矢量:

template<typename C> void mySuperTempalte (std::vector<C> myCont)
{
    //check type of container
    //do something here
}

专门用于双端队列:

template<typename C> void mySuperTempalte (std::deque<C> myCont)
{
    //check type of container
    //do something here
}

【讨论】:

  • 这回答了另一个问题。 ;-)
  • 我认为它确实可以回答,即使它没有尽可能直接和明显。
  • 只有第一个是专业化的;第二种和第三种情况是重载。
  • sep : 您专门针对 vector > - 此代码比 Konrad 的代码更好,但如果调用者使用自定义分配器则会失败。
  • 我仍然认为这个答案虽然可能有帮助,但与问题无关。需要专门化的不是 container,而是它的value_type!这是一个完全不同的问题,需要完全不同的解决方案。
【解决方案2】:

您是否尝试过模板类型名参数?语法有点奇怪,因为它模拟了用于 declare 这样一个容器的语法。有一个很好的 InformIT article 对此进行了更详细的解释。

template <template <typename> class Container>
void mySuperTemplate(Container<std::string> const& cont) {
}

请注意,您还应该将参数声明为引用!

顺便说一句:这条评论

//check type of container

是你做错了什么的一个死的赠品。你确实想检查容器的类型。用户更复杂的重载,如 sep 的答案所示。

【讨论】:

  • 不要使用此代码,因为它不适用于 STL 容器。矢量、列表等不是单参数模板,它们是至少有两个参数的模板,除了第一个参数之外,所有模板都有默认值(允许无限数量的额外参数作为实现细节)。此代码不可移植。
  • @Aaron:是的,感谢您指出这一点。但是,这样做的方法是无论如何都使用迭代器参数而不是容器。
  • 康拉德,好点子。 OP 可能应该告诉我们他真正想要做什么:)
【解决方案3】:

如果我正确理解了您的问题,那么您有一个适用于 STL 容器向量、双端队列等的算法,但正在尝试为字符串编写模板特化。如果是这种情况,那么您可以编写您在问题中定义的通用模板化方法:-

template<typename container> void mySuperTempalte( const container &myCont )
{
    // Implement STL container code
}

然后为你的字符串特化声明:-

template<> void mySuperTempalte( const container<std::string> &myCont )
{
    // Implement the string code
}

对于任何其他专业化,只需更改 myCont 的类型声明。如果您确实需要对 vector 和 deque 容器执行此操作,则将模板参数设置为该容器中类型的参数,而不是像 Sep 建议的容器本身。

template<typename C> void mySuperTempalte( const std::vector<C> &myCont)
{
    // check type of container
    // do something here
}

值得尝试通过使您的第一个实现与所有 STL 容器一起工作以使您的生活更轻松来避免这种情况,然后您只需要对字符串类进行专门化。甚至可以考虑将您的字符串转换为向量以避免一起特化。

附带说明,我已将容器参数更改为 const 引用,我假设这是您想要的,因为无论如何您声明对象 const,这样您就可以避免复制。

【讨论】:

    【解决方案4】:

    到目前为止的答案似乎很有帮助,但我想我会使用不同的结构。我希望所有容器都定义 value_type,就像 STL 容器一样。因此,我可以写

    inline template <typename C> void mySuperTemplate (C const& myCont)
    {
        mySuperTemplateImpl<C, typename C::value_type>(myCont);
    }
    

    一般来说,对明确提取的参数进行操作会更容易。

    【讨论】:

      【解决方案5】:

      @sep

      “简单”解决方案

      'sep' 发布的答案非常好,对于 99% 的应用程序开发人员来说可能已经足够好了,但如果它是库界面的一部分,可以使用一些改进,重复一遍:

      专门用于向量:

      template<typename C> void mySuperTempalte (std::vector<C> myCont)
      {
          //check type of container
          //do something here
      }
      

      如果调用者不使用 std::vector,这将起作用。如果这对你来说足够好,专门用于向量、列表等,那么就在这里停下来使用它。

      更完整的解决方案

      首先,请注意您不能部分专门化函数模板——您可以创建重载。如果它们中的两个或多个匹配程度相同,您将得到“模棱两可的过载”错误。因此,我们需要在您想要支持的每一种情况下都精确匹配。

      执行此操作的一种技术是使用 enable_if 技术 -- enable_if 允许您使用晦涩的语言规则有选择地从可能的匹配列表中取出函数模板重载...基本上,如果某个布尔表达式为假,则重载变为'无形的'。如果您有兴趣,请查阅 SFINAE 了解更多信息。

      示例。这段代码可以用MinGW(g++ parameterize.cpp)或者VC9(cl /EHsc parameterize.cpp)从命令行编译,不会报错:

      #include <iostream>
      #include <vector>
      #include <string>
      using namespace std;
      
      template <bool B, class T> struct enable_if {};
      template <class T> struct enable_if<true, T> { typedef T type; };
      
      template <class T, class U> struct is_same { enum { value = false }; };
      template <class T> struct is_same<T,T> { enum { value = true }; };
      
      namespace detail{
          // our special function, not for strings
          //   use ... to make it the least-prefered overload
          template <class Container>
          void SpecialFunction_(const Container& c, ...){
              cout << "invoked SpecialFunction() default\n";
          }
      
          // our special function, first overload:
          template <class Container>
          // enable only if it is a container of mutable strings
          typename enable_if<
              is_same<typename Container::value_type, string>::value, 
              void
          >::type
          SpecialFunction_(const Container& c, void*){
              cout << "invoked SpecialFunction() for strings\n";
          }
      }
      
      // wrapper function
      template <class Container>
      void SpecialFunction(const Container& c){
          detail::SpecialFunction_(c, 0);
      }
      
      int main(){
          vector<int> vi;
          cout << "calling with vector<int>\n";
          SpecialFunction(vi);
      
          vector<string> vs;
          cout << "\ncalling with vector<string>\n";
          SpecialFunction(vs);
      }
      

      输出:

      d:\scratch>parameterize.exe calling
      with vector<int> invoked
      SpecialFunction() default
      
      calling with vector<string> invoked
      SpecialFunction() for strings
      
      d:\scratch>
      

      【讨论】:

      • 你已经搞定了 :) 我正要使用 boost::enable_if 编写我的答案的“固定”版本,但后来发现如果你有更多的 enable_if,你需要很多对字符串 的特殊处理以及对其他类型的通用支持。但我没有想到省略号的诡计。 +1! :)
      • 怎么样:模板 > class Container > inline std::istream& operator>> (std::istream& in, Container& 容器){ std::string 字符串; while(in>>string){ container.push_back(string); } 返回; }
      • @Martin - 有趣,但不便携。 std::vector 允许有两个以上的模板参数(如果其他参数有默认值),在这种情况下,它不会是模板 类,因此不能推导出来代替 Container。跨度>
      【解决方案6】:

      这是否是一个好的设计还有待进一步讨论。无论如何,您可以使用部分模板特化来检测容器的类型。特别是:

      enum container_types
      {
         unknown,
         list_container,
         vector_container
      };
      
      template <typename T>
      struct detect_container_
      {
         enum { type = unknown };
      };
      
      template <typename V>
      struct detect_container_< std::vector<V> > // specialization
      {
         enum { type = vector_container };
      };
      
      template <typename V>
      struct detect_container_< std::list<V> >
      {
         enum { type = list_container };
      };
      
      // Helper function to ease usage
      template <typename T>
      container_types detect_container( T const & )
      {
         return static_cast<container_types>( detect_container_<T>::type );
      }
      
      int main()
      {
         std::vector<int> v;
      
         assert( detect_container( v ) == vector_container );
      }
      

      【讨论】:

        猜你喜欢
        • 2019-05-24
        • 1970-01-01
        • 2011-06-22
        • 1970-01-01
        • 2013-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-12
        相关资源
        最近更新 更多