3

This example :

#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    int v[] = { 1, 2, 3 };

    std::copy( &v[0], &v[3], std::ostream_iterator< int >( std::cout, "\n " ) );
}

produces next output :

1
 2
 3
 

Is there a way to change the example to make it produce next output?


 1
 2
 3

PS I know I could use the for loop, but I am interested in a solution that uses algorithms and iterators.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • 1
    There's nothing in this question specific to C++11. – Lightness Races in Orbit Sep 08 '11 at 09:07
  • @Tomalak I am using g++ 4.3 (which has very limited c++11 support), therefore I created a c++03 example. But If you can provide a c++11 solution, that is fine as well. (Actually, I would be more interested in a c++11 solution) – BЈовић Sep 08 '11 at 09:09
  • @iammilind Right. fixed it. I created an example with 5 elements in the array, but figured that 3 would be sufficient :) – BЈовић Sep 08 '11 at 09:12
  • In the arguments to the ostream_iterator: Is the additional space after '\n' intentional? In that case you get what you ask for. – André Sep 08 '11 at 09:12
  • 1
    @Andre: The question is about adding a space before the first line, too. – Lightness Races in Orbit Sep 08 '11 at 09:17
  • I am not 100% sure, but I believe that in the current standard `&v[3]` is undefined behavior, and that there was a proposal to change that... at any rate, `v+3` (or `&v[0]+3`) yields the same result and is well defined. The small difference being that in the original operation `&v[3]` you are asking for the address of an object that does not exist: the `v[3]` sub-expression causes undefined behavior. – David Rodríguez - dribeas Sep 08 '11 at 09:29
  • @Tomalak: My bad, I misread his output-line in my RSS reader (I missed the spaces) – André Sep 08 '11 at 09:39
  • @iammilind: Joining an undelete vote just to then re-delete your question goes against the way democracy is supposed to work. How come you deleted your answer, anyway? – Lightness Races in Orbit Sep 08 '11 at 09:55
  • The point is really to change the position of the delimiter (which is "\n " in this example). I managed to properly format the output and expected output – BЈовић Sep 08 '11 at 09:55
  • @David: http://stackoverflow.com/questions/988158/take-the-address-of-a-one-past-the-end-array-element-via-subscript-legal-by-the – Lightness Races in Orbit Sep 08 '11 at 09:56
  • @VJo: I think my answer ventures towards what you are looking for: greater control over the output iterator. – Lightness Races in Orbit Sep 08 '11 at 09:56
  • @Tomalak: After reading yet another piece of info on that particular from the linked question here are my conclusions: In C99 it is well defined (the standard states that `&v[3]` maps to `v+3`, not to `&*(v+3)`), in C++ that is not the case *regardless* of what the accepted answer there states, as it is *assuming* that `&*` cancel out without actually being executed --which might be true in most compilers but is not sanctioned by the C++ standard. I am still not 100% sure, but I can say that I am still 90% that this is UB, and 100% sure that you should avoid the construct just in case. – David Rodríguez - dribeas Sep 08 '11 at 10:27
  • @David: I think you're right. [We should use `&v[0]+3`](http://stackoverflow.com/questions/7346634/dereferencing-an-invalid-pointer-then-taking-the-address-of-the-result) (or just `v+3`, I guess). – Lightness Races in Orbit Sep 08 '11 at 11:01

5 Answers5

3

If you want to use C++11 you can use a lambda.

e.g like this:

int v[] = { 1, 2, 3};

std::for_each( &v[0], &v[3], [](int i){ std::cout << " " << i << "\n";} );
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
mkaes
  • 13,781
  • 10
  • 52
  • 72
  • @Tomalak: The program does exactly what the op wants. I quote the question. Is there a way to change the example to make it produce next output? And my snippet does this. – mkaes Sep 08 '11 at 09:21
3

Use std::cout << " ", instead of std::cout as:

std::copy(v, v+3, std::ostream_iterator<int>(std::cout << " ", "\n " ) );

Here the expression std::cout << " " first evaluates which prints a single space to the output, and the evaluated value which is std::ostream& gets passed to std::ostream_iterator

Now the output will be aligned correctly:

 1
 2
 3

Working code : http://www.ideone.com/kSdpk

By the way, don't write &v[3]. That invokes Undefined bevahior. Write v+3.

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 1
    To clarify, this merely streams a space to `std::cout` when the function argument is evaluated (i.e. before the `copy`); it does not pass `std::cout << " "` to `std::ostream_iterator`. – Lightness Races in Orbit Sep 08 '11 at 09:24
  • 3
    ... and yet it looks so much better than `std::cout << ' '; std::copy(...`... +1 – David Rodríguez - dribeas Sep 08 '11 at 09:30
  • 1
    @Nawaz: I felt that it was an important clarification for anyone who may stumble upon this answer who is not already familiar with the fact. – Lightness Races in Orbit Sep 08 '11 at 09:43
  • @Nawaz: Didn't you just write on http://stackoverflow.com/questions/7346634/dereferencing-an-invalid-pointer-then-taking-the-address-of-the-result that `&v[3]` is UB? :) – Lightness Races in Orbit Sep 08 '11 at 11:03
  • @Tomalak: It was just copy paste from the original question, and didn't see much other part of his code, other than `stc::cout`. Anyway, I suggested him what to do to avoid UB. – Nawaz Sep 08 '11 at 11:10
3

There is no way to do this with std::ostream_iterator. (IMHO, there should be, but it's not there.) If you don't mind writing an extra small function or class, you can use std::transform, e.g.:

struct FormatString
{
    std::string operator()( std::string const& original ) const
    {
        return ' ' + original + '\n';
    }
};

//  ...
std::transform(
    v.begin(), v.end(),
    std::ostream_iterator<std::string>( std::cout ), 
    FormatString() );

If you have C++11, you can use a lambda for the FormatString.

I find the need for this occurs often enough that I've written a PatsubstTransformer—a functional object which basically implements the $(patsubst...) function of GNU make. So I would just have to write:

std::transform(
    v.begin(), v.end(),
    std::ostream_iterator<std::string>( std::cout ),
    PatsubstTransformer( "%", " %\n" ) );

I find I use this a lot. (I also find using std::transform more appropriate than std::copy, since what I'm outputting is a transformation.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
2

Output a single space before the std::copy.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Read the last line of post : I think this is absolutely not what he asked for. – psycho Sep 08 '11 at 09:15
  • @psycho: I think that it most certainly is. – Lightness Races in Orbit Sep 08 '11 at 09:19
  • @Tomalak : reading at the post title and the last line, I really don't think so. I think he's totally able to produce such a simple output, the question seems to be more about the use of `std::ostream_iterator`. This solution is just a workaround, as the @mkaes answer is. But I won't flame, let him answer. – psycho Sep 08 '11 at 09:23
  • @psycho: If he were able to produce "such a simple output", then he wouldn't be asking "is there a way to change the example to make it produce [that] output?" – Lightness Races in Orbit Sep 08 '11 at 09:25
  • @Tomalak : I guess you're right, the question is not what it should be. But your last answer (about ostream_iterator being configurable) is far more adapted to the post, to my mind. – psycho Sep 08 '11 at 09:30
  • @psycho: Thanks. I added an example now, too. – Lightness Races in Orbit Sep 08 '11 at 09:53
2

No, not really. ostream_iterator isn't configurable like that.

So you'll have to use the pre-space "workaround" as found in other answers, and manually chop off that final line.


BTW It's been noted that &v[3], strictly speaking, invokes undefined behaviour due to the implicit dereference in the sub-expression v[3]. Prefer &v[0]+3 (or just v+3) — "having" a pointer to one-past-the-end of an array is okay, as long as it's not dereferenced.


You could make your own kind of ostream_iterator that does this, as the following example demonstrates.

Yes, it's verbose; however, you can also change it around however you like to suit your changing needs:

#include <iostream>
#include <iterator>
#include <algorithm>
 
template <class T, class charT = char, class traits = std::char_traits<charT> >
struct ostream_iterator_x
  : std::iterator<std::output_iterator_tag, void, void, void, void> {
    
    typedef charT char_type;
    typedef traits traits_type;
    typedef std::basic_ostream<charT,traits> ostream_type;
    
    ostream_iterator_x(ostream_type& s, const charT* pre = 0, const charT* post = 0)
       :    s(s)
       ,  pre(pre)
       , post(post) {};
    
    ostream_iterator_x(const ostream_iterator_x& x)
       :    s(x.s)
       ,  pre(x.pre)
       , post(x.post) {};
    
    ~ostream_iterator_x() {}
    
    ostream_iterator_x& operator=(const T& value) {
       if (pre  != 0) s << pre;
       s << value;
       if (post != 0) s << post;
       
       return *this;
    }
    
    ostream_iterator_x& operator*()     { return *this; }
    ostream_iterator_x& operator++()    { return *this; }
    ostream_iterator_x& operator++(int) { return *this; }
    
  private:
    ostream_type& s;
    const charT* pre;
    const charT* post;
};
 
int main()
{
    int v[] = { 1, 2, 3 };
    std::copy(v, v+3, ostream_iterator_x<int>(std::cout, " ", "\n"));
}

// Output:
//  1
//  2
//  3

(I used [n3290: 24.6/2] to determine the members and base-specification required for this to work and to be standard-compliant.)

Live demo.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Good idea! Simple and efficient. (Already upvoted this morning, but would deserve another one) – psycho Sep 08 '11 at 14:19
  • @psycho: Heh, thanks :) In hindsight I think James's approach with `std::transform` makes more sense, though. – Lightness Races in Orbit Sep 08 '11 at 14:28
  • yup, just saw it. For this case yes, fits perfectly, but I generally prefer a solution which is a little more general than specific... so both are valuable to me. – psycho Sep 08 '11 at 14:41