cout << vector<string>

B

barcaroller

In the boost::program_options tutorial, the author included the following
code:


cout << "Input files are: "
<< vm["input-file"].as< vector<string> >()
<< "\n";


Basically, he is trying to print a vector of string, in one line. I could
not get this compile (let alone run). To get it to compile and run, I had
to re-write it as:


vector<string> v = vm["input-file"].as< vector<string> >();

vector<string>::const_iterator i;

for ( i = v.begin();
i != v.end();
++i )
{
cout << *citer << " ";
}


Does anyone know if the original line of code is correct? If so, what was I
missing?
 
M

Maxim Yegorushkin

In the boost::program_options tutorial, the author included the following
code:

    cout << "Input files are: "
         << vm["input-file"].as< vector<string> >()
         << "\n";

Basically, he is trying to print a vector of string, in one line.  I could
not get this compile (let alone run).  To get it to compile and run, I had
to re-write it as:

    vector<string> v = vm["input-file"].as< vector<string> >();

    vector<string>::const_iterator i;

    for (   i  = v.begin();
            i != v.end();
          ++i )
    {
        cout << *citer << " ";
    }

Does anyone know if the original line of code is correct?  If so, what was I
missing?

The example probably assumes there is an overloaded operator<<() for
std::eek:stream and std::vector<>, something like this:

#include <ostream>
#include <iterator>
#include <algorithm>

namespace std {

template<class A1, class A2>
ostream& operator<<(ostream& s, vector<A1, A2> const& vec)
{
copy(vec.begin(), vec.end(), ostream_iterator<A1>(s, "
"));
return s;
}

}
 
J

Juha Nieminen

Jeff said:
template<typename T>
std::eek:stream& operator<<(std::eek:stream& out, std::vector<T> const& v) {
if (!v.empty()) {
typedef std::eek:stream_iterator<T> out_iter;
copy(v.begin(), v.end() - 1, out_iter( out, " " ));
out << v.back();
}
return out;
}

Is there some advantage of that code over a shorter and simpler:

template<typename T>
std::eek:stream& operator<<(std::eek:stream& out, std::vector<T> const& v) {
for(std::size_t i = 0; i < v.size()-1; ++i)
out << v << " ";
out << v.back();
return out;
}
 
K

Kai-Uwe Bux

Juha said:
Is there some advantage of that code over a shorter and simpler:

template<typename T>
std::eek:stream& operator<<(std::eek:stream& out, std::vector<T> const& v) {
for(std::size_t i = 0; i < v.size()-1; ++i)

Nit: std::size_t is not guaranteed to be vector::size_type.
out << v << " ";
out << v.back();


I think, v.back() has undefined behavior if the v is empty.
return out;
}


Best

Kai-Uwe Bux
 
J

James Kanze

The correct alternative, AIUI:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
template<typename T>
std::eek:stream& operator<<(std::eek:stream& out, std::vector<T> const& v) {
if (!v.empty()) {
typedef std::eek:stream_iterator<T> out_iter;
copy(v.begin(), v.end() - 1, out_iter( out, " " ));
out << v.back();
}
return out;
}
int main() {
int const ints[] = { 1, 2, 3, 4 };
std::cout << std::vector<int>( ints, ints + 4 ) << '\n';
}

Correct in what sense? It's OK for simple test, like this, but
it's certainly not something you'd allow in production code.
 
K

Kai-Uwe Bux

rio said:
i know max(std::size_t) >= max(vector::size_type)

Could you provide a pointer into the standard to backup that claim? or are
you making a statement about a particular platform?

i know v.size()-1<=max(vector::size_type) <= max(std::size_t)

so size_t is ok


Best

Kai-Uwe Bux
 
M

Maxim Yegorushkin

Which has undefined behavior. You can only add template specializations
to namespace std when they depend on user-defined types.

Formally true.

On practice it works just fine as long as One Definition Rule is not
violated.
 
J

James Kanze

James said:
Pete Becker wrote:
On 2008-11-07 06:03:15 -0500, Maxim Yegorushkin
<[email protected]> said:
The example probably assumes there is an overloaded operator<<() for
std::eek:stream and std::vector<>, something like this:
namespace std {
template<class A1, class A2>
ostream& operator<<(ostream& s, vector<A1, A2> const& vec)
Which has undefined behavior. You can only add template
specializations to namespace std when they depend on
user-defined types.
The correct alternative, AIUI:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
template<typename T>
std::eek:stream& operator<<(std::eek:stream& out, std::vector<T> const& v) {
if (!v.empty()) {
typedef std::eek:stream_iterator<T> out_iter;
copy(v.begin(), v.end() - 1, out_iter( out, " " ));
out << v.back();
}
return out;
}
int main() {
int const ints[] = { 1, 2, 3, 4 };
std::cout << std::vector<int>( ints, ints + 4 ) << '\n';
}
Correct in what sense? It's OK for simple test, like this,
but it's certainly not something you'd allow in production
code.
Correct in the sense that it implements the OP's intent, and
is allowed by the standard. As far as using it in production
code... Ad hoc output of this sort only really comes up for
debugging purposes. If you don't even think it's OK for
debug, I'd love to know your suggested alternative.

Do you leave such debugging code in your production code? If
not, no problem, but there's no point in making the << a
template. If so:

-- It doesn't work. Try it with std::vector<
std::vector< int > >, for example.

-- It very rapidly introduces undefined behavior, as you write
the above, and some other programmer provides the same
function, but with a comma as a separator.

For a quick debugging session, the simplest solution is to knock
out a non-template version in an unnamed namespace. For
anything else, you really need to define what is wanted; you
don't output raw vectors, but vectors with a semantic
signification, which determines how you format them. If you're
using vectors to represent both sets and sequences in a
mathematical context, for example, you'll doubtlessly have a
class for each, with the vector buried deep down in it. And
those classes will provide the formatted output.

If you find that you often need such debugging output (I don't,
but I'm sure that there are applications that to), then you
might want to come up with a general FormattedSequence class
template, with a template function to generate it (so you get
automatic type deduction); this could, of course, be in any
namespace you want.
 
J

James Kanze

Formally true.
On practice it works just fine as long as One Definition Rule
is not violated.

Taking into account all of the code, including that which you
can't see or know about, such as in the implementation of the
library.

In other words, it works just fine as long as you're lucky.
 
M

Maxim Yegorushkin

Taking into account all of the code, including that which you
can't see or know about, such as in the implementation of the
library.
Precisely.

In other words, it works just fine as long as you're lucky.

Not quite. Rather as long as one understands what he is doing.
 
J

James Kanze

i know max(std::size_t) >= max(vector::size_type)

If this is true, then g++, VC++ and both of the library
implementations used with Sun CC are buggy, since they all allow
me to instantiate std::vector with an allocator which typedef's
size_type to unsigned long long.
i know  v.size()-1<=max(vector::size_type) <= max(std::size_t)

Given that v.size() returns a vector::size_type, it's safe to
say that v.size() <= max( vector::size_type ). But if
vector::size_type is an unsigned long long, and size_t is only
an unsigned long or an unsigned int, the second part is false.

Depending on what you're doing, it may be acceptable to suppose
that the default allocator is being used---I use size_t for this
a lot, when I know the actual instantiation of vector. But it's
not acceptable in a generic library.
 
J

James Kanze

Not quite. Rather as long as one understands what he is doing.

And has access to, and has read and understood all of the source
code of the standard library he's using, and can guarantee that
his code will never be compiled using a compiler where this is
not the case.
 
M

Maxim Yegorushkin

And has access to, and has read and understood all of the source
code of the standard library he's using, and can guarantee that
his code will never be compiled using a compiler where this is
not the case.

You just confirmed that no luck involved here, this is just a question
of understanding. ;)
 
M

Maxim Yegorushkin

[]
  Having followed this whole battle of words, I wonder what's
  wrong with putting this operator into the global namespace
  and altogether avoiding the hassle of having to know about
  things you're not supposed to know about?

Nothing wrong and this is indeed the correct way to do so. I confused
this case with another one, sincere apologies.
 
M

Maxim Yegorushkin

[]
  Having followed this whole battle of words, I wonder what's
  wrong with putting this operator into the global namespace
  and altogether avoiding the hassle of having to know about
  things you're not supposed to know about?

Nothing wrong and this is indeed the correct way to do so. I confused
this case with another one, sincere apologies.

This what I was confusing it with:

#include <map>
#include <iostream>
#include <iterator>

// should be in namespace std::
template<class T, class U>
std::eek:stream& operator<<(std::eek:stream& s, std::pair<T, U> const&
p)
{
return s << p.first << ' ' << p.second;
}

int main()
{
typedef std::map<int, int> Map;
Map m;
std::copy(
m.begin()
, m.end()
, std::eek:stream_iterator<Map::value_type>(std::cout)
);
}

It won't compile unless operator<<(std::eek:stream& s, std::pair<T, U>
const& p) is in namespace std.
 
M

Maxim Yegorushkin

Maxim said:
[]
  Having followed this whole battle of words, I wonder what's
  wrong with putting this operator into the global namespace
  and altogether avoiding the hassle of having to know about
  things you're not supposed to know about?
Nothing wrong and this is indeed the correct way to do so. I confused
this case with another one, sincere apologies.
This what I was confusing it with:
    #include <map>
    #include <iostream>
    #include <iterator>
    // should be in namespace std::
    template<class T, class U>
    std::eek:stream& operator<<(std::eek:stream& s, std::pair<T, U> const& p)
    {
        return s << p.first << ' ' << p.second;
    }
    int main()
    {
        typedef std::map<int, int> Map;
        Map m;
        std::copy(
              m.begin()
            , m.end()
            , std::eek:stream_iterator<Map::value_type>(std::cout)
            );
    }
It won't compile unless operator<<(std::eek:stream& s, std::pair<T, U>
const& p) is in namespace std.

  I would have asked the same question for this code. :)
  I don't understand why it doesn't compile. It comes down
  to this
    ostr << val;
  with 'ostr' being an 'std::basic_ostream<>' and 'val'
  being an 'std::pair<>'. Why doesn't this find the global
  operator?

Because expression "ostr << val" is template argument dependent and
thus is bound at the second phase of the two-phase name lookup. At the
second phase it uses ADL only to search for functions within
namespaces associated with ostr and val. ostr is std::basic_ostream
and val is std::pair<int, int>, thus one associated namespace is std.
int has no associated namespaces. So, the only namespace considered
for expression "ostr << val" is std, which lacks a suitable
operator<<().
 
M

Maxim Yegorushkin

Maxim said:
Maxim Yegorushkin wrote: [...]
    #include <map>
    #include <iostream>
    #include <iterator>
    // should be in namespace std::
    template<class T, class U>
    std::eek:stream& operator<<(std::eek:stream& s, std::pair<T, U> const& p)
    {
        return s << p.first << ' ' << p.second;
    }
    int main()
    {
        typedef std::map<int, int> Map;
        Map m;
        std::copy(
              m.begin()
            , m.end()
            , std::eek:stream_iterator<Map::value_type>(std::cout)
            );
    }
It won't compile unless operator<<(std::eek:stream& s, std::pair<T, U>
const& p) is in namespace std.
  I would have asked the same question for this code. :)
  I don't understand why it doesn't compile. It comes down
  to this
    ostr << val;
  with 'ostr' being an 'std::basic_ostream<>' and 'val'
  being an 'std::pair<>'. Why doesn't this find the global
  operator?
Because expression "ostr << val" is template argument dependent and
thus is bound at the second phase of the two-phase name lookup. At the
second phase it uses ADL only to search for functions within
namespaces associated with ostr and val. ostr is std::basic_ostream
and val is std::pair<int, int>, thus one associated namespace is std.
int has no associated namespaces. So, the only namespace considered
for expression "ostr << val" is std, which lacks a suitable
operator<<().

  But lookup isn't ADL only. The enclosing namespaces are considered,
  too, aren't they? And the global namespaces is always enclosing.
  (I'm not saying you're wrong. I just don't understand this.)

At the second stage of the two-phase name lookup (at the point of
template instantiation) it is ADL only.
 
M

Maxim Yegorushkin

Maxim said:
Maxim Yegorushkin wrote:
Maxim Yegorushkin wrote:
[...]
    #include <map>
    #include <iostream>
    #include <iterator>
    // should be in namespace std::
    template<class T, class U>
    std::eek:stream& operator<<(std::eek:stream& s, std::pair<T, U> const& p)
    {
        return s << p.first << ' ' << p.second;
    }
    int main()
    {
        typedef std::map<int, int> Map;
        Map m;
        std::copy(
              m.begin()
            , m.end()
            , std::eek:stream_iterator<Map::value_type>(std::cout)
            );
    }
It won't compile unless operator<<(std::eek:stream& s, std::pair<T, U>
const& p) is in namespace std.
  I would have asked the same question for this code. :)
  I don't understand why it doesn't compile. It comes down
  to this
    ostr << val;
  with 'ostr' being an 'std::basic_ostream<>' and 'val'
  being an 'std::pair<>'. Why doesn't this find the global
  operator?
Because expression "ostr << val" is template argument dependent and
thus is bound at the second phase of the two-phase name lookup. At the
second phase it uses ADL only to search for functions within
namespaces associated with ostr and val. ostr is std::basic_ostream
and val is std::pair<int, int>, thus one associated namespace is std.
int has no associated namespaces. So, the only namespace considered
for expression "ostr << val" is std, which lacks a suitable
operator<<().
  But lookup isn't ADL only. The enclosing namespaces are considered,
  too, aren't they? And the global namespaces is always enclosing.
  (I'm not saying you're wrong. I just don't understand this.)
At the second stage of the two-phase name lookup (at the point of
template instantiation) it is ADL only.

  I'm trying to come up with some trivial example that
  illustrates this, but I fail. I have this

     #include <iostream>

     namespace N {
         struct test { };

         template< typename T >
         void f(T) {  std::cout << "f(T)\n"; }

         void f(int) {  std::cout << "f(int)\n"; }
     }

     template< typename T >
     void g(T o) { f(o); }

     int main()
     {
         N::test t;
         g(t);
     }

  which compiles fine and gives the expected result.
  What am I still missing.

Your example should work just fine.

Here is a simplified version of the problem with
std::eek:stream_iterator<std::pair<> > and a global
operator<<(std::eek:stream&, std::pair<>):

namespace N {

struct X {};

void bar(struct overload_for_compilers_with_no_two_phase_lookup&);

template<class T> void foo(T t) { bar(t); }

}

template<class T> void bar(T);

int main()
{
N::X x;
foo(x);
}
 
K

Kai-Uwe Bux

rio said:
ok
what about relation between "size_t"
and 'std::vector<T>::size_type'?

It's not dealt with anywhere. As far as I know, all we know about
std::size_t is that it is unsigned and the return type of the sizeof()
operator. Whether it can hold any value that std::vector<T>::size_type can
hold, I have not seen in the standard (neither the C standard, which says
nothing about vector<T>::size_type nor the C++ standard).


Best

Kai-Uwe Bux
 
J

James Kanze

It's not dealt with anywhere. As far as I know, all we know
about std::size_t is that it is unsigned and the return type
of the sizeof() operator. Whether it can hold any value that
std::vector<T>::size_type can hold, I have not seen in the
standard (neither the C standard, which says nothing about
vector<T>::size_type nor the C++ standard).

Well, vector<T>::size_type has to be an unsigned integral type,
capable of representing all of the positive values of
vector<T>::difference_type. And vector<T>::difference_type must
be the type returned by subtracting two iterators,

I'm almost sure that the intent was that vector<T>::size_type
should be then same as the size_type of it's allocator; this is,
at least, what all of the implementations I know do. Which
means that it can be pretty much anything the user wants (as
long as it is an unsigned integral type); the default allocator
is required to typedef it to size_t. (The current standard
states that it should be "a type that can represent the size of
the largest object in the allocation model." This is certainly
wrong, since it is incompatible with the requirement that it
must contain all of the positive values which the
difference_type can contain---I've used systems where the
largest single object was 65365, but where pointers were
effectively 20 bits, and the difference between two pointers
could be 1 MB.) All of this has been rewritten in the current
draft to be expressed in terms of concepts, so someone has a lot
of detailed reading to do between here and Jan. 15th (when I
have to submit my comments on the CD to AFNOR).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top