Overloading output stream operator for vector<T>
Asked Answered
R

6

23

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.

Rianon answered 2/11, 2010 at 12:28 Comment(3)
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?Cenac
C++11 syntax: for (auto &i : vec) {} make code shorterMehta
@Charles It should be for(const auto&i:vec) to compile.Ethiopic
G
7

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?

Gangland answered 2/11, 2010 at 12:35 Comment(6)
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.Rianon
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?Rianon
@Leonid, yes that would be great that someones explain the concept of dependent name.Noctambulism
@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...Gangland
@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...Gangland
<< is nice because it also works for vector<vector<vector<double>>> rather then writing 2 for_each/range loopsHalide
S
16

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

Sherman answered 2/11, 2010 at 12:46 Comment(1)
A more modern way to do to this would be to use for range loop from C++ 11 instead of const_iteratorApgar
G
7

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?

Gangland answered 2/11, 2010 at 12:35 Comment(6)
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.Rianon
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?Rianon
@Leonid, yes that would be great that someones explain the concept of dependent name.Noctambulism
@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...Gangland
@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...Gangland
<< is nice because it also works for vector<vector<vector<double>>> rather then writing 2 for_each/range loopsHalide
B
2
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;
}
Boling answered 2/11, 2010 at 12:36 Comment(3)
can someone tell me, why I cannot <pre><code> tag this piece of code ?Boling
because SO uses a modified version of "markdown". Indent by 4 spaces for formatted code.Dilatant
warning: signed in compared to unsigned int :(Hephzipah
N
1

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

Noctambulism answered 2/11, 2010 at 12:34 Comment(21)
Inlining is orthogonal to templatizing.Bubal
The template is declared in the header. If the header is included in multiple translational units there will be multiply defined symbols. Hence inline.Rianon
you don't need to put typename in front of "const std::vector<T>& v"Sherman
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.Noctambulism
@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.Salmonoid
@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.Noctambulism
@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.Noctambulism
@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.Bubal
@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.Bubal
@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: #2487937 – The VC++ compiler accepts this code because it’s just broken.Salmonoid
@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.Bubal
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.Salmonoid
@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.Bubal
@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).Salmonoid
@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.Bubal
@Konrad inline is not required for templates. The ODR allows to omit it.Ionia
@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).Ionia
What he actually wanted to write is const typename ... or typename ... const instead of typename const ... though, the last of which is syntactically illegal.Ionia
@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 :-)Noctambulism
@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.Salmonoid
@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.Ionia
A
0

For people coming into this thread after C++11, use the ranged for loop to make the code easier to read.

template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
  for (const auto &x : v) {
    os << '[' << x << ']';
  }
  return os;
}
Aprylapse answered 15/2, 2022 at 6:45 Comment(1)
You add a ] between each elementApgar
O
0

Here goes a contribution with an operator<< overload for std::vector example using for_each from the standard library algorithm (https://en.cppreference.com/w/cpp/algorithm/for_each).

#include <algorithm>
#include <vector>
#include <iostream>

template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> vec) {

    // printing the first element using at so it is bound checked
    // also, this helps to make the print prettier
    os << "{" << vec.at(0) << "}";

    // using for_each to go from second to last element of vec
    std::for_each(std::cbegin(vec)+1, std::cend(vec), [&](auto e){os << ", {" << e << "}";});

    return os;
}

int main() {
    std::vector<int> vec;

    vec.push_back(1);
    vec.push_back(2);

    std::cout << vec << std::endl;
}
Officialdom answered 4/6, 2023 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.