【问题标题】:Overloading output stream operator for vector<T>重载 vector<T> 的输出流运算符
【发布时间】:2011-05-03 22:03:34
【问题描述】:

重载输出流运算符的推荐方法是什么?以下可以完成。如果没有为类型 T 定义运算符

template < class T >
inline std::ostream& operator << (std::ostream& os, const std::vector<T>& v) 
{
    os << "[";
    for (std::vector<T>::const_iterator ii = v.begin(); ii != v.end(); ++ii)
    {
        os << " " << *ii;
    }
    os << " ]";
    return os;
}

编辑:它确实编译,问题不相关并且在命名空间中。感谢您的帮助。

【问题讨论】:

  • 能否详细说明命名空间问题及其解决方案?当参数类型来自 std 时,ADL 将无法在全局命名空间中找到这样的重载函数,并且您不能将其放入 std。你是怎么解决的?
  • C++11 语法:for (auto &amp;i : vec) {} 使代码更短
  • @Charles 编译应该是for(const auto&amp;i:vec)

标签: c++ templates vector operator-overloading


【解决方案1】:

这就是你想要的:

template < class T >
std::ostream& operator << (std::ostream& os, const std::vector<T>& v) 
{
    os << "[";
    for (typename std::vector<T>::const_iterator ii = v.begin(); ii != v.end(); ++ii)
    {
        os << " " << *ii;
    }
    os << "]";
    return os;
}

你忘记了第一个ostream的std::

您在os &lt;&lt; "[" 中的[ 之后添加了一个额外的空格。

std::vector&lt;T&gt;::const_iterator之前需要typename

【讨论】:

    【解决方案2】:

    你真的尝试过这段代码吗?它在 gcc 上运行良好,只需稍加调整 std::vector&lt;T&gt;::const_iterator,需要声明为 typename std::vector&lt;T&gt;::const_iterator

    使用 std::copy 和 std::ostream_iterator 可能会更好。

    编辑:类型、依赖类型和类型名 无法将其全部放入 cmets,所以就这样吧(顺便说一句。这是我的理解,我可能会离开一个国家英里 - 如果是这样,请纠正我!)...

    我认为这最好用一个简单的例子来解释..

    假设你有一个函数 foo

    template <typename T>
    void foo()
    {
      T::bob * instofbob; // this is a dependent name (i.e. bob depends on T)
    };
    

    看起来不错,通常你可以这样做

    class SimpleClass
    {
      typedef int bob;
    };
    

    然后调用

    foo<SimpleClass>(); // now we know that foo::instofbob is "int"
    

    再一次,似乎不言自明,但是一些 nuser 出现并这样做

    class IdiotClass
    {
      static int bob;
    };
    

    现在

    foo<IdiotClass>(); // oops, 
    

    您现在拥有的是一个表达式(乘法),因为 IdiotClass::bob 解析为非类型!

    对于人类来说,很明显这是愚蠢的,但是编译器无法区分类型和非类型,并且默认情况下在 C++ 中(我认为这是编译器不同的地方),all 合格的依赖名称(即T::bob)将被视为非类型。要明确告诉编译器依赖名称是一个真实类型,你必须指定typename关键字 -

    template <typename T>
    void foo()
    {
      typedef typename T::bob *instofbob; // now compiler is happy, it knows to interpret "bob" as a type (and will complain otherwise!)
    };
    

    即使是typedef,这也适用。即

    template <typename T>
    void foo()
    {
      typedef typename T::bob local_bob;
    };
    

    这样更清楚吗?

    【讨论】:

    • 为什么std::copy 在泛型向量的情况下是一种更好的流出方式?对于std::copy,应该写一个专门的长行来输出一个向量。但是,使用输出流运算符更容易做到:std::cout &lt;&lt; "Assignments = " &lt;&lt; assignmentIds &lt;&lt; std::endl;。我想在大括号"[" + vector + "]" 内打印一个向量,如果使用std::copy,那将需要多一行仅包含“]”。
    • 在解决原始问题后,它确实可以在没有 typename 的情况下进行编译。在这种情况下指定typename 是否有任何用处,即使它确实可以编译,但存在类型被误解的轻微风险?
    • @Leonid,是的,有人解释从属名称的概念会很棒。
    • @Leonid,在上面的简单向量示例的情况下,同意 std::copy 操作看起来很冗长,但现在考虑您决定更改格式,并且您希望它是在某些情况下由“,”分隔,在其他情况下由“”分隔,您将如何使用单个全局运算符非常警惕这样的全球运营商...
    • @Leonid,关于你的第二点,如果你的编译器很高兴并且你很高兴你永远不必在另一个编译器中重新编译你的代码,那么我就不会打扰;但是为了一个关键词“typename”,如果你曾经在编译器之间移动,你可以省去一些麻烦,试图理解由于省略它而得到的相当冗长的编译器错误消息!这是你的电话...
    【解决方案3】:
    template<typename T>
    std::ostream& operator<<(std::ostream& s, std::vector<T> t) { 
        s << "[";
        for (std::size_t i = 0; i < t.size(); i++) {
            s << t[i] << (i == t.size() - 1 ? "" : ",");
        }
        return s << "]" << std::endl;
    }
    

    【讨论】:

    • 谁能告诉我,为什么我不能
       标记这段代码?
    • 因为 SO 使用了“markdown”的修改版本。格式化代码缩进 4 个空格。
    • warning: signed in compared to unsigned int :(
    【解决方案4】:

    这是在 Visual Studio 2003 上为我编译的。 当然你应该在const std::vector&lt;T&gt;之前使用关键字typename 而且我认为inline 关键字没有意义,恕我直言模板非常接近内联。

    #include <ostream>
    #include <vector>
    #include <iostream>
    
    template < class T >
    std::ostream& operator << (std::ostream& os, typename const std::vector<T>& v) 
    {
        os << "[ ";
        for (typename std::vector<T>::const_iterator ii = v.begin(); ii != v.end(); ++ii)
        {
            os << " " << *ii;
        }
        os << "]";
        return os;
    }
    
    void Test()
    {
        std::vector<int> vect;
        vect.push_back(5);
        std::cerr << vect;
    }
    

    编辑:按照 Nim 的建议,我还在 std::vector&lt;T&gt;::const_iterator 之前添加了一个 typename

    【讨论】:

    • 内联与模板化是正交的。
    • @Konrad inline 不是模板所必需的。 ODR 允许省略它。
    • @Konrad C++ 标准说“关键字类型名应仅应用于限定名称,但这些名称不必依赖。”。 C++03 还强制“关键字类型名只能在模板声明和定义中使用”,但 C++0x 将删除它。综上所述,std::vector&lt;T&gt; 绝对是一个从属名称。您仍然不需要在这里需要 typename,因为编译器可以在解析时查找std::vector 并查看它是一个类模板(因此知道std::vector&lt;T&gt; 是一个类型)。
    • 他真正想写的是const typename ...typename ... const而不是typename const ...,最后一个在语法上是非法的。
    • @Konrad vector&lt;T&gt; 不合格,但 std::vector&lt;T&gt; 合格。对于内联的东西,请参阅最后的 3.2 项目符号列表(我目前这里没有标准),其中提到了类内联函数以及函数/类模板。
    【解决方案5】:

    对于在 C++11 之后进入此线程的人,请使用范围 for 循环以使代码更易于阅读。

    template <class T>
    std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
      for (const auto &x : v) {
        os << '[' << x << ']';
      }
      return os;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-03
      • 1970-01-01
      • 1970-01-01
      • 2021-07-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多