Writing operator<< for std::vector<int>

  • Follow


Sorry if this was discussed earlier, but Google Groups doesn't allow you 
to search on "operator<<" (it ignores the "<<").  Anyhow, how does one 
define "operator<<" in, say, util.h, such that the following code works 
in all situations:

#include "util.h"

// ... Some time later in a function or method ...
{
     std::vector<int> v;
     v.push_back(1);
     v.push_back(2);
     v.push_back(3);
     std::cout << "My vector: " << v << std::endl;
}

To at least partially answer my question, here are two answers I've come 
up with after doing some research.

Answer #1) Write util.h like this:

#include <vector>
#include <iosfwd>

namespace std {

std::ostream & operator<<(std::ostream & s, const std::vector<int> v);

}

It's necessary to put it in the std namespace due to name hiding and 
Koenig lookup.  You cannot put it in any other namespace (include the 
global namespace).

Answer #2) You can't.

This is because you *must* put it in the std namespace to work, but 
putting stuff into the std namespace is potentially illegal.  Say a 
future version of C++ provides an operator<< for std::vector<int>.  My 
code will no longer compile.  And I probably deserved it because I was 
making additions to a namespace I did not control.

This has two important consequences, though.  First, I cannot add 
operator<< to any class that does not already provide one.  Since 
operator<< must be part of the namespace in which the class is defined, 
doing so would be making additions to a namespace I do not control. 
Second, I cannot add operator<< for a vector of my own classes.  Say I 
have Object1 in namespace ns1, I cannot put operator<< for 
std::vector<ns1:Object1> in namespace ns1 because Koenig lookup requires 
it to go in std.  Sure, if I put it in ns1, then I can use it within 
ns1.  But a user of ns1 cannot use it since it could be in, say, 
namespace ns2.

Okay, that's my current understanding, and it would be nice to have 
someone with more knowledge look over my thought process.  Are my 
answers and consequences correct?  Are there other explanations and 
possibilities I have missed?

Thanks,

-Dave

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Dave 2/18/2005 3:10:41 AM

"Dave Dribin" <yhdrib@yahoo.com> wrote in message
news:CNednUOUXOuQWYnfRVn-uQ@speakeasy.net...
> putting stuff into the std namespace is potentially illegal.  Say a
> future version of C++ provides an operator<< for std::vector<int>.  My
> code will no longer compile.  And I probably deserved it because I was
> making additions to a namespace I did not control.
>
> This has two important consequences, though.  First, I cannot add
> operator<< to any class that does not already provide one.  Since
> operator<< must be part of the namespace in which the class is defined,
> doing so would be making additions to a namespace I do not control.

You're not supposed to put stuff into namespace std.  You are supposed to
define functions in your own namespace, even if your functions make
reference
to things from std.  As you know, it's common to add an operator<< that
deals with ostreams, and this can be defined in the same namespace as
the user-defined class to be operated on.

> Second, I cannot add operator<< for a vector of my own classes.  Say I
> have Object1 in namespace ns1, I cannot put operator<< for
> std::vector<ns1:Object1> in namespace ns1 because Koenig lookup requires
> it to go in std.  Sure, if I put it in ns1, then I can use it within
> ns1.  But a user of ns1 cannot use it since it could be in, say,
> namespace ns2.

So you want to add operator<< to a vector specialization?  You can't do
something like that to a generic container, only to the things it contains,
assuming you're the one defining the things it contains. Koenig lookup is
right.

If you want to define operator<< for a vector<X> you can do it.  Simply wrap
the vector<X> with your own custom container class and define operator<<
for that class.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply Mike 2/18/2005 11:09:06 AM


Dave Dribin wrote:

> Sorry if this was discussed earlier, but Google Groups doesn't
> allow you to search on "operator<<" (it ignores the "<<").
> Anyhow, how does one define "operator<<" in, say, util.h, such
> that the following code works in all situations:

> #include "util.h"

> // ... Some time later in a function or method ...
> {
>      std::vector<int> v;
>      v.push_back(1);
>      v.push_back(2);
>      v.push_back(3);
>      std::cout << "My vector: " << v << std::endl;
> }

> To at least partially answer my question, here are two answers
> I've come up with after doing some research.

> Answer #1) Write util.h like this:

> #include <vector>
> #include <iosfwd>

> namespace std {

> std::ostream & operator<<(std::ostream & s, const std::vector<int>
v);

> }

> It's necessary to put it in the std namespace due to name
> hiding and Koenig lookup.  You cannot put it in any other
> namespace (include the global namespace).

Correct.  While formally illegal (undefined behavior), there's a
good chance of it working on any given compiler.

> Answer #2) You can't.

> This is because you *must* put it in the std namespace to
> work, but putting stuff into the std namespace is potentially
> illegal.  Say a future version of C++ provides an operator<<
> for std::vector<int>.

More to the point, an implementation today has the right to
provide such a version for it's internal use.

IMHO, the probability of such is pretty slight.

> My code will no longer compile.  And I probably deserved it
> because I was making additions to a namespace I did not
> control.

> This has two important consequences, though.  First, I cannot
> add operator<< to any class that does not already provide one.
> Since operator<< must be part of the namespace in which the
> class is defined, doing so would be making additions to a
> namespace I do not control.

That seems pretty normal to me.  A feature, rather than a
defect.  (Well, it would be a feature if it guaranteed an error
message.)

You cannot simply go around adding operator<< for arbitrary
types.  What happens if someone else in your project does the
same thing ; you end up with two, incompatible operator<< for
the same type.

> Second, I cannot add operator<< for a vector of my own
> classes.

> Say I have Object1 in namespace ns1, I cannot put operator<<
> for std::vector<ns1:Object1> in namespace ns1 because Koenig
> lookup requires it to go in std.  Sure, if I put it in ns1,
> then I can use it within ns1.  But a user of ns1 cannot use it
> since it could be in, say, namespace ns2.

Why not?  If you have an std::vector< ns1::Object1 > as a
parameter, Koenig lookup will search in both std and ns1.

But again, I'm sceptical of having an << defined for a vector.
One person will want commas as separators, another just spaces,
and a third semi-colons.  If you are using the vector in a
specific case, where only one form of output makes sense (or it
makes sense for the user to chose via manipulators), then you
should wrap the vector in a user defined class anyway, for type
safety reasons.

For quick hacks and experimenting, of course, wrapping in a
class may be overkill.  But for quick hacks and experimenting,
since you know that in fact, putting the operator in std::
works, just do it.  It's not as if you have to maintain the
code.

--
James Kanze                                           GABI Software
Conseils en informatique orient�e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S�mard, 78210 St.-Cyr-l'�cole, France, +33 (0)1 30 23 00 34


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply kanze 2/19/2005 6:06:12 AM

Dave Dribin wrote:
> Anyhow, how does one define "operator<<" in, say, util.h, such that
> the following code works in all situations:

Dave,

Here is one way to do it.  Define a wrapper class around the object you
want to output (here, std::vector<int>).  This wrapper can be written
as a template so you can wrap any type.  Then you call operator<< on
the wrapped object.


#include <iostream>
#include <vector>

template<typename T>
struct Printer {

  // The default behaviour is just to call the object's operator<<,
  // so e.g. if we pass T=int, this still works.
  // Of course the point is that we override this default by
  // specialising the Printer class for types which don't have
  // an operator<< (see below)
  static void print(const T& object, std::ostream& out)
  { out << object; }
};


// This class is used to temporarily wrap the object you want to
// output
template<typename T>
class OutputWrapper {

  const T* pobj;

  public:

    OutputWrapper(const T& object)
:  pobj(&object)
    { }

    friend std::ostream& operator<<
    (std::ostream& out, const OutputWrapper& wrapper)
    { Printer<T>::print(*wrapper.pobj, out); return out; }
};

// A helper function for creating the output wrapper
template<typename T>
OutputWrapper<T> make_output_wrapper(const T& object)
{ return OutputWrapper<T>(object); }


// Specialise for any type of std::vector
// This requires that operator<< is defined for X, i.e. the type of
// object stored in the vector
template<typename X>
struct Printer< std::vector<X> > {

  static void print(const std::vector<X>& v, std::ostream& out) {

    for(typename std::vector<X>::const_iterator
      i = v.begin(), end = v.end(); i != end; ++i) {

      out << *i;
      if(i != end-1) out << ", ";
    }
  }
};


int main() {

  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);

  std::cout << make_output_wrapper(v) << std::endl;
}


This should print

1, 2

btw, the reason we need two classes (Printer and OutputWrapper) is that
we only want to specialise one behaviour (i.e. the printing).  If we
subsumed Printer into OutputWrapper we'd have to repeat all the
boilerplate code (constructor, declaration of *pobj) for each value of
T.

Gareth


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Gareth 2/19/2005 6:38:33 AM

3 Replies
209 Views

(page loaded in 0.108 seconds)

Similiar Articles:













7/27/2012 6:23:18 PM


Reply: