22

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.

Leonid
  • 22,360
  • 25
  • 67
  • 91
  • 4
    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? – Jeremy W. Murphy Sep 08 '13 at 08:21
  • 2
    C++11 syntax: `for (auto &i : vec) {}` make code shorter – C.W. Jan 08 '17 at 17:13
  • 1
    @Charles It should be `for(const auto&i:vec)` to compile. – Walter Jan 20 '17 at 16:25

6 Answers6

15

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

Jason Iverson
  • 2,500
  • 1
  • 15
  • 15
  • A more modern way to do to this would be to use `for range loop` from C++ 11 instead of `const_iterator` – Tudax May 02 '22 at 14:03
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?

Nim
  • 33,299
  • 2
  • 62
  • 101
  • 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 Nov 02 '10 at 13:34
  • 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? – Leonid Nov 02 '10 at 13:44
  • @Leonid, yes that would be great that someones explain the concept of dependent name. – Stephane Rolland Nov 02 '10 at 14:00
  • @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 Nov 02 '10 at 14:08
  • @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... – Nim Nov 02 '10 at 14:13
  • << is nice because it also works for vector>> rather then writing 2 for_each/range loops – kirill_igum Apr 30 '13 at 20:12
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;
}
Grimeh
  • 161
  • 1
  • 12
smartnut007
  • 6,324
  • 6
  • 45
  • 52
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

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • 1
    Inlining is orthogonal to templatizing. – sbi Nov 02 '10 at 12:39
  • The template is declared in the header. If the header is included in multiple translational units there will be multiply defined symbols. Hence inline. – Leonid Nov 02 '10 at 12:40
  • you don't need to put typename in front of "const std::vector& v" – Jason Iverson Nov 02 '10 at 12:41
  • 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`. – Stephane Rolland Nov 02 '10 at 12:45
  • @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. – Konrad Rudolph Nov 02 '10 at 12:51
  • @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 is not a dependent name of T ? I would like to understand further. – Stephane Rolland Nov 02 '10 at 13:48
  • @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. – Stephane Rolland Nov 02 '10 at 14:15
  • @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 Nov 02 '10 at 14:44
  • @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. – sbi Nov 02 '10 at 14:46
  • @Stephane: when it sees the code, the compiler knows that `vector` is a type (it can’t be anything else here). However, wenn it sees `vector::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: http://stackoverflow.com/questions/2487437/c-defining-a-static-member-of-a-template-class-with-type-inner-class-pointer – The VC++ compiler accepts this code because it’s just broken. – Konrad Rudolph Nov 02 '10 at 14:48
  • @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. – sbi Nov 02 '10 at 14:49
  • 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. – Konrad Rudolph Nov 02 '10 at 14:51
  • @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. – sbi Nov 02 '10 at 16:31
  • @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). – Konrad Rudolph Nov 02 '10 at 20:29
  • @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 bool operator== (const array& x, const array& 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. – sbi Nov 02 '10 at 22:36
  • 2
    @Konrad `inline` is not required for templates. The ODR allows to omit it. – Johannes Schaub - litb Nov 02 '10 at 23:20
  • 1
    @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` 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` is a type). – Johannes Schaub - litb Nov 02 '10 at 23:24
  • 1
    What he actually wanted to write is `const typename ...` or `typename ... const` instead of `typename const ...` though, the last of which is syntactically illegal. – Johannes Schaub - litb Nov 02 '10 at 23:50
  • @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 :-) – Stephane Rolland Nov 03 '10 at 08:59
  • @Johannes: `vector` 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. – Konrad Rudolph Nov 03 '10 at 09:59
  • 1
    @Konrad `vector` isn't qualified, but `std::vector` 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. – Johannes Schaub - litb Nov 03 '10 at 11:53
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;
}
Rase
  • 1
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;
}
griloHBG
  • 177
  • 1
  • 14