Overloading output stream operator for vector<T>

23,248

Solution 1

Did you actually try this code? It works fine on gcc with a small tweak std::vector<T>::const_iterator, needs to be declared as typename std::vector<T>::const_iterator

You may be better off with using std::copy and std::ostream_iterator.

EDIT: types, dependent types and typename Can't fit it all in the comments, so here goes (btw. this is my understanding and I could be off by a country mile - if so please correct me!)...

I think this is best explained with a simple example..

Let's assume you have a function foo

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

Looks okay, and typically you may do this

class SimpleClass
{
  typedef int bob;
};

And call

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

Again, seems self explanatory, however some nuser comes along and does this

class IdiotClass
{
  static int bob;
};

Now

foo<IdiotClass>(); // oops, 

What you have now is an expression (multiplication) as IdiotClass::bob resolves to a non-type!

To the human, it's obvious that this is stupid, but the compiler has no way of differentiating between types vs. non-types, and by default in C++ (and I think this is where compilers differ), all qualified dependent names (i.e. T::bob) will be treated as non-type. To explicitly tell the compiler that the dependent name is a real type, you must specify the typename keyword -

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!)
};

This applies even if it is a typedef. i.e.

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

Is that any clearer?

Solution 2

This is what you want:

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;
}

You forgot the std:: on the first ostream

You put an extra space after [ in os << "[".

and you need typename before std::vector<T>::const_iterator

Solution 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;
}

Solution 4

this compile for me on visual studio 2003. surely youshould use the keyword typename before the const std::vector<T> and I don't think the inline keyword has sense, IMHO templates are really close to inlining.

#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;
}

Edit: I have added a typename also before the std::vector<T>::const_iterator as Nim suggested

Share:
23,248

Related videos on Youtube

Leonid
Author by

Leonid

Software Developer / Problem-Solver.

Updated on February 15, 2022

Comments

  • Leonid
    Leonid about 2 years

    What is a recommended way to overload the output stream operator? The following can not be done. It is expected that compilation will fail if the operator << is not defined for a type 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;
    }
    

    EDIT: It does compile, the problem was unrelated and was in the namespace. Thanks for assistance.

    • Jeremy W. Murphy
      Jeremy W. Murphy over 10 years
      Can you elaborate on the namespace problem and its solution? An overloaded function like this in the global namespace won't be found by ADL when the argument type is from std, and you can't put it in std. How did you solve it?
    • C.W.
      C.W. over 7 years
      C++11 syntax: for (auto &i : vec) {} make code shorter
    • Walter
      Walter over 7 years
      @Charles It should be for(const auto&i:vec) to compile.
  • smartnut007
    smartnut007 over 13 years
    can someone tell me, why I cannot <pre><code> tag this piece of code ?
  • sbi
    sbi over 13 years
    Inlining is orthogonal to templatizing.
  • Leonid
    Leonid over 13 years
    The template is declared in the header. If the header is included in multiple translational units there will be multiply defined symbols. Hence inline.
  • Jason Iverson
    Jason Iverson over 13 years
    you don't need to put typename in front of "const std::vector<T>& v"
  • Stephane Rolland
    Stephane Rolland over 13 years
    With visual 2003 I didn't need none of the typename so as to compile. As it was a type dependent of T, I think it can please some other compiler to specify typename.
  • Steve Jessop
    Steve Jessop over 13 years
    because SO uses a modified version of "markdown". Indent by 4 spaces for formatted code.
  • Konrad Rudolph
    Konrad Rudolph over 13 years
    @Stephane: no. Your first typename is actually an error and hence the code won’t work on other compilers either. You must only specify typename before dependent type names. And as sbi and Leonid have said, inline is actually required here if this code is inside a header.
  • Leonid
    Leonid over 13 years
    Why would std::copy be a better way of streaming out in case of a generic vector? With std::copy a dedicated long line should be written just to stream out a vector. However with output stream operator it is easier to do: std::cout << "Assignments = " << assignmentIds << std::endl;. I would like to print a vector always inside braces "[" + vector + "]", and that would require one more line with just "]" if std::copy is used.
  • Leonid
    Leonid over 13 years
    It does compile without typename after solving the original problem. Is there any use of specifying typename in this context even if it does compile apart from slight risk of having a misinterpreted type?
  • Stephane Rolland
    Stephane Rolland over 13 years
    @Konrad, It DOES compile on VC2003. Maybe the VC compiler is wrong of course. But could you explain please WHY is it an error ? Why std::vector<T> is not a dependent name of T ? I would like to understand further.
  • Stephane Rolland
    Stephane Rolland over 13 years
    @Leonid, yes that would be great that someones explain the concept of dependent name.
  • Nim
    Nim over 13 years
    @Leonid, in the case of the trivial vector example above, agreed the std::copy operation appears to be verbose, but consider now that you decide to change the formatting, and you want it to be separated by a ',' in some instances and by a ' ' in others, how would you have that with a single global operator<<? Now let's imagine that someone else is re-using your code, and wants to do something different? I'd be very wary of such global operators...
  • Nim
    Nim over 13 years
    @Leonid, as to your second point, if your compiler is happy and you're happy that you're never going to have to re-compile your code in another compiler then I wouldn't bother; but for the sake of one key word "typename", if you are ever in the position of moving between compilers, you can save yourself some headache trying to understand the rather verbose compiler error messages you get as a result of leaving it out! It's your call...
  • Stephane Rolland
    Stephane Rolland over 13 years
    @Konrad, unless implicitely instanciated in a module/compilation unit, I conceive NO WAYS for a template not to be multiply defining symbols in each units. I mean it NO WAYS: Could you explain what the use of the keyword inline does for template. I personnaly call templates in headers inlined templates, this is why I think Inline and Templates are probably not Orthogonal... maybe not parralelle ;-) but not orthogonal.
  • Inverse
    Inverse over 13 years
    warning: signed in compared to unsigned int :(
  • sbi
    sbi over 13 years
    @Leonid: Function templates must be defined in the header. Just as inlined functions, they won't cause a linker error, because the compiler is required to sort this out for you.
  • sbi
    sbi over 13 years
    @Stephane: About 15 years after it has been thought up, VC still doesn't properly implement two-phase lookup. Don't rely on VC when it comes to where a typename (or template) is required.
  • Konrad Rudolph
    Konrad Rudolph over 13 years
    @Stephane: when it sees the code, the compiler knows that vector<T> is a type (it can’t be anything else here). However, wenn it sees vector<T>::something it cannot assume that something is a type without knowing the type of T since vector may be specialized for a different T to contain e.g. a member function called something. See here: stackoverflow.com/questions/2487437/… – The VC++ compiler accepts this code because it’s just broken.
  • sbi
    sbi over 13 years
    @Stephane: Assuming your comment about inlining refers to me, not to Konrad: inline asks the compiler to substitute function calls with the actual code of the function called. template doesn't do this. Of course, the compiler is free to inline or not no matter whether you asked it. Still, function templates do not need to be inlined. How the implementation deals with the resulting problem is their headache not yours. Many compilers will emit specially marked code for their instantiations that the linker then folds.
  • Konrad Rudolph
    Konrad Rudolph over 13 years
    As for why inline is needed, consider you’ve got a header that is included in two different .cpp files. The header contains a non-inline function foo. Now you compile both .cpp files and link them against each other (two objects to form one executable). Each of the .cpp files contains its own copy of foo. When you link them together this will result in a “duplicate definition” error because the linker sees two identical definitions of foo. And this behaviour is regardless of whether the function was actually a template.
  • sbi
    sbi over 13 years
    @Konrad: No, I think you're wrong here, inline is not needed. Templates ought to be in headers (except if using export, which, sadly, is gone) and always needed to be. However, they do not need to be inline. Your implementation will take care of that. Some implementations (I think cfront did that) defer template compilation until the link phase, others (I think VC does so) allow the linker to fold identical instantiations which resulted from instantiating the same template in multiple TUs. But __function templates do not need to be inline and, TTBOMK, never needed to.
  • Konrad Rudolph
    Konrad Rudolph over 13 years
    @sbi: In this case I really am 100% sure that you are mistaken and that I’m correct. I’ve been involved in a generic library (SeqAn) for the past few years and this is one of the fundamental technical quirks we have to deal with. And as I’ve said, this is unrelated to templates: functions need to be declared inline in headers (and since templates need to be defined in headers, so do they).
  • sbi
    sbi over 13 years
    @Konrad: I can only believe that we have a serious misunderstanding here. In the last decade, I have probably written hundreds of function templates, very many of which I have not marked inline, all used across several compilers and a myriad of compiler versions. I have seen even more. The first thing I found was boost/array.hpp. It has the free function template template<class T, std::size_t N> bool operator== (const array<T,N>& x, const array<T,N>& y) {...} (that's a very old version of boost, I came across, in case something has changed). Note the absence of inline. As I knew.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    @Konrad inline is not required for templates. The ODR allows to omit it.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    @Konrad the C++ Standard says "The keyword typename shall be applied only to qualified names, but those names need not be dependent.". C++03 in addition enforces "The keyword typename shall only be used in template declarations and definitions", but C++0x is going to remove that. That all said, std::vector<T> definitely is a dependent name. Still you don't need typename here, because the compiler can lookup std::vector at parse time and see that it is a class template (and thus know that std::vector<T> is a type).
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    What he actually wanted to write is const typename ... or typename ... const instead of typename const ... though, the last of which is syntactically illegal.
  • Stephane Rolland
    Stephane Rolland over 13 years
    @Johannes Schaub thanks for your Expert clarifications. That was what I needed in our case. But however illegal typename const seems not to have offended much my VC2003 :-). I'll write things according Standard now :-)
  • Konrad Rudolph
    Konrad Rudolph over 13 years
    @Johannes: vector<T> may be a dependent name but it isn’t a qualified name. You not only don’t need typename here, it’s illegal (§14.6.5: “… typename shall only be applied to qualified names”). And at least g++ enforces this. But it’s true that typename can be applied to names that “need not be dependent”. – As for the inline thing, wow. It actually works (again, in g++) … the library has operated on false premises for years! Just for reference though, can you provide the section in the standard that allows inline to be omitted from function templates? I can’t find it.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    @Konrad vector<T> isn't qualified, but std::vector<T> is. For the inline thing, see 3.2 the bullet list at the very end (I don't have the standard here currently), mentioning among other things classes inline functions and also function/class templates.
  • kirill_igum
    kirill_igum about 11 years
    << is nice because it also works for vector<vector<vector<double>>> rather then writing 2 for_each/range loops
  • Tudax
    Tudax about 2 years
    A more modern way to do to this would be to use for range loop from C++ 11 instead of const_iterator
  • Tudax
    Tudax about 2 years
    You add a ] between each element