【问题标题】:How to make function argument container independent如何使函数参数容器独立
【发布时间】:2011-07-24 00:07:43
【问题描述】:

我正在编写一个实用函数,它将采用元素向量(可以是字符串、整数、双精度、字符)并连接成单个字符串并返回它。它看起来像这样:

template<typename T>
std::string convert2Str(std::vector<T> const& vec) 
{
   std::ostringstream sStream; 
   for (size_t k=0; k<vec.size(); ++k) {
      sStream << vec[k] << " "; 
   }
   return sStream.str(); 
}

我想让这个函数更通用:

  • 首先使用迭代器而不是使用vector&lt;T&gt; 的索引。我试过这个 std::vector&lt;T&gt;::const_iterator it = vec.begin() 在循环之前和编译器给了我一个错误: : 错误: 预期 ; 在它之前 当我将上述定义更改为std::vector&lt;std::string&gt;::const_iterator it = vec.begin() 时,错误消失了。所以,看起来我没有遵循正确的语法,请告诉我它是什么
  • 第二个是通过使第一个参数容器独立来使函数更通用。给定任何容器(vectorlistqueuedeque 等)我想做和上面一样的事情。我尝试在 stackoverflow 中搜索,但没有找到满意的答案。

【问题讨论】:

    标签: c++ templates iterator


    【解决方案1】:

    第 1 步,如您所说,使用迭代器:

    template<typename T>
    std::string convert2Str(std::vector<T> const& vec) 
    {
       typedef std::vector<T> container;
       std::ostringstream sStream; 
       for (typename container::const_iterator it = vec.begin(); it != vec.end(); ++it) {
          sStream << *it << " "; 
       }
       return sStream.str(); 
    }
    

    第 2 步,将模板参数设为容器类型而不是元素类型(您可以通过 value_type 获取元素类型:

    template<typename container>
    std::string convert2Str(container const& vec)
    {
       typedef container::value_type T; // if needed
       std::ostringstream sStream; 
       for (typename container::const_iterator it = vec.begin(); it != vec.end(); ++it) {
          sStream << *it << " "; 
       }
       return sStream.str(); 
    }
    

    在 C++0x 中,这变得更加简单(并且不需要 typename):

    template<typename container>
    std::string convert2Str(container const& vec)
    {
       using std::begin;
       using std::end;
       std::ostringstream sStream;
       for (auto it = begin(vec); it != end(vec); ++it) {
          typedef decltype(*it) T; // if needed
          sStream << *it << " "; 
       }
       return sStream.str(); 
    }
    

    除其他优点外,std::beginstd::end 适用于原始数组。

    【讨论】:

    • 他的第 1 点和你的有什么区别。使用 std::vector::const_iterator 不起作用,但类型定义它可以吗?我很困惑。他的解决方案只是在迭代器声明前缺少一个类型名。 (正如编译器所说)但是为什么呢?感觉就像是在非类型定义类型前面的 c 中缺少一个结构。
    • @Ronny:这里需要 typename 关键字。 typedef 只是用来简化向通用容器的过渡(Tcontainer 仍然被定义,但我切换了哪个是模板参数)。
    • 我猜“typedef container::value_type T; // if needed”这行告诉编译器容器是向量、列表等,并确保不能用简单的方法调用convert2Str数据类型,例如 int 或 double。那么,为什么这个声明被标记为“如果需要”..感谢您的及时回答。
    • @srikrish:那一行只会让你回到原来代码中存在的T 类型。这个函数没有在任何地方使用T,但我想告诉你如何访问它以防万一。即使没有这一行,如果您尝试传入不是容器的内容,编译器也会抱怨对 beginend 的调用。
    • 所以如果 T 是用户定义的类型(我自己的类),我可以做这样的事情吗 sStream
    【解决方案2】:

    按照 STL 实践,我建议对输入参数使用两个迭代器,而不是一个容器(原因很明显,它只能使用容器的一部分,并且通常使用迭代器定义的任何序列):

    template<typename InputIterator>
    std::string convert2Str(InputIterator first, InputIterator last)
    {
        std::ostringstream sStream;
        for (InputIterator it = first; it != last; ++it) {
           sStream << *it << " ";
        }
        return sStream.str();
    }
    

    如果您需要包含对象的类型,请使用

    typedef typename std::iterator_traits<InputIterator>::value_type T;
    

    添加: 然后你可以使用如下函数:

    std::vector<int> int_vec;
    std::list<float> f_list;
    std::deque<std::string> str_deq;
    
         // put something into the containers here
    
    std::cout<< convert2Str(int_vec.begin(), int_vec.end()) <<std::endl;
    std::cout<< convert2Str(f_list.begin(), f_list.end()) <<std::endl;
    std::cout<< convert2Str(str_deq.begin(), str_deq.end()) <<std::endl;
    

    请注意,您不能迭代 std::queue;但如果您真的需要它,该标准可以保证对自己动手的解决方案提供足够的支持。在此处查看更多信息:std::queue iteration

    【讨论】:

      【解决方案3】:

      如果您只对容器类型进行模板化,那是最简单的;值类型作为typedef 成员value_type 存储在所有标准、Boost 和Qt 容器中。 std::copyostream_iterator 允许您跳过冗长的迭代器声明。

      template <typename Container>
      std::string convert2Str(Container const &cont)
      {
          std::ostringstream s;
          std::copy(cont.begin(), cont.end(),
                    std::ostream_iterator<typename Container::value_type>(s, " "));
          return s.str();
      }
      

      typename 是避免歧义所必需的。当您省略此关键字时,最新版本的 GCC 会警告您。

      【讨论】:

      • 当然,这实际上并没有教srikrish如何编写自己的通用函数,这也是一项有用的技能。而ostream_iterator&lt;decltype(*cont.begin())&gt; 使用 C++0x 会更好。
      • @Ben: decltype 令人困惑;看起来你正在运行/评估一个表达式,而实际上你不是。任何符合 STL 的容器都会有 value_type(标准容器有,Boost 容器有,甚至 Qt 中的容器也有)。
      • @larsman:抱歉,应该是ostream_iterator&lt;decltype(*begin(cont))&gt;。原始数组没有value_type,但它们可以与std::beginstd::end 一起使用。绝对应该是 using std::copy; copy(...); 让 Koenig 查找来做它的事情。请记住,函数模板不能部分特化,并且禁止在 namespace std 中定义新的重载,为容器提供优化版本的 copy 的唯一方法是使用 ADL。
      • * 此代码是否可移植到诸如 solaris 之类的平台?我最近在我的代码中使用了 std::count ,而 solaris 中的 sun 编译器对此有所抱怨。你能解释一下这个语句 "std::ostream_iterator(s, " ")" 是做什么的吗?谢谢
      • @srikrish:它适用于任何符合标准的 C++ 编译器,但我不知道 Sun 编译器是否符合标准。见cplusplus.com/reference/std/iterator/ostream_iterator
      【解决方案4】:

      使用这个。您需要 typename 部分来告诉编译器它应该在解析时考虑 T::const_iterator 类型,直到您实际调用传递一些具有 @ 的 T 的函数之前,它才能真正知道这是真的987654324@会员类型。

      template<typename T>
      std::string convert2Str(T const& cont) 
      {
          std::ostringstream sStream; 
          for (typename T::const_iterator it = cont.begin(); it != cont.end(); ++it) {
              sStream << *it << " "; 
          }
          return sStream.str(); 
      }
      

      【讨论】:

      • typename 的好点子。还有一个尽快切换到 C++0x 的理由。
      【解决方案5】:

      我认为这应该可行:

      template<typename T>
      std::string convert2Str(T const& container) 
      {
         std::ostringstream sStream; 
         for (typename T::const_iterator i= container.begin(); i != container.end(); ++i) {
            sStream << *i << " "; 
         }
         return sStream.str(); 
      }
      

      演示:http://ideone.com/9pUVV

      【讨论】:

        猜你喜欢
        • 2012-07-22
        • 2010-11-30
        • 1970-01-01
        • 2017-03-30
        • 2018-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多