Why Does Ostream_Iterator Not Work as Expected

Why does ostream_iterator not work as expected?

The problem is that the name lookup does not find your operator<<(ostream& os, const PAIR& r). The code that tries to invoke the operator<< is in somewhere inside the ostream_iterator<> which is itself inside the std namespace. The name lookup looks around for the right function inside ostream_iterator<> and the std namespace; the argument dependent lookup does not help here because both of the parameters are in the std namespace, too.

So, my suggestion is (1) either to wrap your operator into namespace std { }, but that is UB, IIRC. Or (2) create a struct inheriting from std::pair to define a new type in your namespace, and using the ADL to find your operator<<().

UPDATE:

My 3rd suggestion is to use a custom manipulator to print out the pair.

As for my 2nd suggestion, if you can use C++11, inheriting from std::pair should be easy (untested):

struct PAIR : std::pair
{
using std::pair::pair;
};

If you cannot use C++11, then I suggest using a custom manipulator.

std::ostream_iterator does not find operator

std::copy cannot find overloading for operator << for std::pair in std namespace. There is no good way, to overload operator << for object from std namespace in algorithms from std namespace.

You can use std::for_each with functor, that will print your values, for example with lambda.

std::for_each(data.begin(), data.end(), [](const std::pair<int, int>& p)
{
std::cout << p << std::endl;
});

You cannot put overloading in std namespace, you can only add specializations for user-defined types since


The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a
namespace within namespace std unless otherwise specified

A program may add a template specialization
for any standard library template to namespace std only if the declaration depends on a user-defined type
and the specialization meets the standard library requirements for the original template and is not explicitly
prohibited.

template argument for ostream_iterator-each element is pair

There is NO operator << for std::pair. You cannot simply use ostream_iterator with std::pair.

You can use other things, or write class, that derived from pair, or that store pair and use it. You cannot write overloads in std namespace, since it leads to undefined behaviour and you cannot overload this operator in global namespace, since ADL will not find correct overload (if you use stl algorithms, like copy, ostream_iterator).

Simply, something like this will work well

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

int main()
{
std::vector<std::pair<int, int>> vec =
{
{1,1},
{2,2}
};
for (const auto& p : vec)
{
std::cout << p.first << " " << p.second << std::endl;
}
}

Dereferencing and increment with ostream_iterator

Conceptually, when writing to a range, you'd want to move to the next element after writing to one. For most iterators, like e.g. a std::vector::iterator, that has to be done explicitly. So it kind of makes sense to include it, if only for consistency.

In the particular case of std::ostream_iterator, it does not have an actual effect though, and could be left out. You cannot overwrite an "element" of the output range anyways, advancing is implicit (and only implicit, i.e. both the increment operators as well as derefencing are no-ops in this context).

The important part is only the operator =, as explained in the relevant documentation:

#include <iostream>
#include <iterator>

int main()
{
std::ostream_iterator<int> i1(std::cout, ", ");
*i1++ = 1; // usual form, used by standard algorithms
*++i1 = 2;
i1 = 3; // neither * nor ++ are necessary
std::ostream_iterator<double> i2(std::cout);
i2 = 3.14;
}

Why can't I instantiate operator (ostream&, vector T &) with T=vector int ?

Two words: name lookup.

Here is a simplified example of what you are trying to do, without any Standard Library headers required:

template <typename T> void f(T) { }

namespace ns {
class C { };

void f(int) { }

void test() { f(C()); } // doesn't work :'(
}

int main() {
f(ns::C()); // works! :-D
}

In this example, in main(), the only f that is found during normal name lookup is the function template in the global namespace, and it matches, so main uses it (ns::f is also found during argument-dependent lookup, but it isn't a match so the global f is still selected during overload resolution).

In test, however, the ns::f(int) overload is found and name lookup stops. Namespaces are searched outwards, so ns is searched first, then the global namespace, but name lookup stops once a name is found, so once ns::f(int) is found, name lookup stops. Argument-dependent lookup also takes place and also finds ns::f(int), since C is in namespace ns, then ADL stops searching.

The same is true in your example: in main(), the operator<< overload is found, but inside of the std::ostream_iterator, which is in the std namespace, other << overloads are found, and so your overload is not found.

Your operator<< overload would need to be in the std namespace for it to work, but unfortunately you aren't allowed to add names to the std namespace.

Overload for ostream on vectors throws error when using std::copy

Because your operator<< is not visible to std entities.

Note std::ostream_iterator<T> outputs values as if through operator<<, and according to [temp.dep.res]/1:

In resolving dependent names, names from the following sources are
considered:

  • Declarations that are visible at the point of definition of the template.
  • Declarations from namespaces associated with the types of the function arguments both from the instantiation context ([temp.point])
    and from the definition context.

... your operator<< is neither visible at the point of definition of std::ostream_iterator<T>, nor in the namespace std, so the operator<< used in std::ostream_iterator<T> cannot be correctly resolved.

ostream_iterator for Binary Output

It works, but you will have to explicitely use an ostream_iterator<char>.

Example (includes omitted for brievety):

int main(int argc, char **argv) {
std::vector<int> arr;

std::ofstream fd("foo.txt", std::ios::binary | std::ios::out);

for (int i=0; i<256; i++) arr.push_back(i);

std::ostream_iterator<char> oi(fd);
std::copy(arr.begin(), arr.end(), oi);
fd.close();
return 0;
}

will write the 256 bytes for 0 to 255 in foo.txt.


Above assumed that you wanted to write directly the value of the int as a char to the file. From your comment, you want to write the int value as a 4 bytes value (assuming int32_t) in native host endianness. I would use an auxilliary class to do the job:

class bint {
private:
char c[sizeof(int)];

public:
bint(const int i) { // allows bint b = 5;
::memcpy(c, &i, sizeof(c));
}
operator int() const { // allows : bint b = 5; int i=b => gives i=5
int i;
::memcpy(&i, c, sizeof(int));
return i;
}
const char *getBytes() const { // gives public read-only access to the bytes
return c;
}
};

std::ostream& operator <<(std::ostream& out, const bint& b) {
out.write(b.getBytes(), sizeof(int));
return out;
}

int main(int argc, char **argv) {
std::vector<int> arr;

std::ofstream fd("foo.txt", std::ios::binary | std::ios::out);

for (int i=0; i<256; i++) arr.push_back(i);

std::ostream_iterator<bint> oi(fd);
std::copy(arr.begin(), arr.end(), oi);
fd.close();
return 0;
}

This one writes 1024 bytes (for integer of size 4) containing the representations of the 256 first integers. It would automatically adapts to other sizes of int.

STL remove doesn't work as expected?

Actually std::remove doesn't remove the item from the container. Quoted from here

Remove removes from the range [first, last) all elements that are equal to value. That is, remove returns an iterator new_last such that the range [first, new_last) contains no elements equal to value. The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove is stable, meaning that the relative order of elements that are not equal to value is unchanged.`

That is, std::remove works with a pair of iterators only and does not know anything about the container which actually contains the items. In fact, it's not possible for std::remove to know the underlying container, because there is no way it can go from a pair of iterators to discover about the container to which the iterators belong. So std::remove doesn't really remove the items, simply because it cannot. The only way to actually remove an item from a container is to invoke a member function on that container.

So if you want to remove the items, then use Erase-Remove Idiom:

 v.erase(std::remove(v.begin(), v.end(), 10), v.end()); 

The erase-remove idiom is so common and useful is that std::list has added another member function called list::remove which produces the same effect as that of the erase-remove idiom.

 std::list<int> l;
//...
l.remove(10); //it "actually" removes all elements with value 10!

That means, you don't need to use erase-remove idiom when you work with std::list. You can directly call its member function list::remove.



Related Topics



Leave a reply



Submit