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: cannot write std::map via ostream_iterator? - comp.lang.c++ ...... code below typed from memory): std::ostream &operator<<(std::ostream &o, const std::pair<int,int ... from std::pair, and an operator<< to write it to std ... nitializing a static vector <> of integers (this static vectorWriting operator for std::vector - comp.lang.c++.moderated ..... vector<int ... ostream_iterator-like class ... nitializing a static vector > of integers ... vector is ... does vector::resize throw bad_alloc exception? - comp.lang.c++ ...Writing operator for std::vector - comp.lang.c++.moderated ... does vector::resize throw bad_alloc exception? - comp.lang.c++ ... For example: std::vector<int> v; //will ... writing robust software? - comp.lang.c++.moderatedThere are some general rules how to write robust ... std::vector<int> x(5); starts off with five ... Writing operator for std::vector - comp.lang.c++.moderated ... std::stringstream and string to unsigned long conversion - comp ...... comp ... how to deal with unsigned long long int? - comp.lang.asm.x86 ... std ... Writing operator for std::vector - comp.lang.c++.moderated ... std::stringstream and ... Why no std::back_insert_iterator::value_type? - comp.lang.c++ ...// Deleted headers. using namespace std; int main() { typedef vector<int ... ostream_iterator-like class that is able to write onto a stream any type for which operator ... Const constructor - comp.lang.c++.moderated... include <vector> void mutate(const std::vector<int ... an array with copy semantics (like std::vector) or one can write a ... a(&a) {} virtual double operator[](int ... Non-member operator overloading, linker complains - comp.lang.c++ ...... vector> template<typename T, unsigned int d, unsigned int e> class Matrix { private: friend Matrix<T, d, e> operator-( const Matrix<T, d, e>& m ); typedef std::vector ... How to write testbench file? - comp.lang.vhdl... logic_vector (8 downto 1); signal TB_PRDATA: std_logic_vector (8 downto 1); signal TB_INT: std ... How to write testbench file? - comp.lang.vhdl This is called a self ... Problems with a std::vector (begin and end): Different behavior ...Problems with a std::vector ... const char *tag, int id, const std::vector ... writing robust software? - comp.lang.c++.moderated... statement as in: std::vector<T ... more sense ... Writing operator<< for std::vector - comp.lang.c++.moderated ...Sorry if this was discussed earlier, but Google Groups doesn't allow you to search on "operator v; v.push_back(1); v.push_back(2); v.p... C++ Tutorial: A Beginner's Guide to std::vector, Part 1std::vector<int> v; // declares a vector of integers. For ... Because int is a built-in type, writing all 10 elements with operator [] would actually work, but we would ... 7/27/2012 6:23:18 PM
|