Problem with defining operator<< for std::ostream_iterator

K

krzysztof.konopko

I cannot compile the code which defines a std::map type consisting of
built in types and operator<< overload for std::map::value_type.
See the code below - I attach a full example.

Note: if I define map type with my new type (structure) everything is
OK. All compileres I've cheked report an error so I think it is a
problem with my code.

#include <algorithm>
#include <cstddef>
#include <fstream>
#include <iterator>
#include <map>

class A
{
struct S // behavior similar to built-in int
{
int i;
S( int _i ) : i( _i ) {}
};

typedef std::map< int, int > Map1;
typedef std::map< int, S > Map2;

// the function below cannot be found by
std::eek:stream_iterator< Map1::value_type >
friend std::eek:stream &operator<<( std::eek:stream &_ostr, const
Map1::value_type &_value )
{
return _ostr << _value.first << ' ' << _value.second;
}

friend std::eek:stream &operator<<( std::eek:stream &_ostr, const
Map2::value_type &_value )
{
return _ostr << _value.first << ' ' << _value.second.i;
}

Map1 m_map1;
Map2 m_map2;

public:
A(
const int *_keyStart,
const int *_valueStart,
const std::size_t _size
)
{
for ( std::size_t i = 0; i < _size; i++ )
{
m_map1.insert( Map1::value_type( _keyStart, _valueStart ) );
m_map2.insert( Map2::value_type( _keyStart,
S( _valueStart ) ) );
}
}

~A()
{
std::eek:fstream out( "test" );

out << "#### Map1 ####" << std::endl;
// this call does not compile
std::copy(
m_map1.begin(),
m_map1.end(),
std::eek:stream_iterator< Map1::value_type >( out, "\n" )
);

out << "\n#### Map2 ####" << std::endl;
std::copy(
m_map2.begin(),
m_map2.end(),
std::eek:stream_iterator< Map2::value_type >( out, "\n" )
);
}
};

int main()
{
int tab1[] = { 1, 3, 5, 2, 0 };
int tab2[ sizeof tab1 / sizeof *tab1 ] = { 1, 2, 3, 4, 5 };

A a( tab1, tab2, sizeof tab1 / sizeof *tab1 );
}
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

I cannot compile the code which defines a std::map type consisting of
built in types and operator<< overload for std::map::value_type.
See the code below - I attach a full example.

Note: if I define map type with my new type (structure) everything is
OK. All compileres I've cheked report an error so I think it is a
problem with my code.

#include <algorithm>
#include <cstddef>
#include <fstream>
#include <iterator>
#include <map>

class A
{
struct S // behavior similar to built-in int
{
int i;
S( int _i ) : i( _i ) {}
};

typedef std::map< int, int > Map1;
typedef std::map< int, S > Map2;

// the function below cannot be found by
// std::eek:stream_iterator< Map1::value_type >
friend std::eek:stream &operator<<(
std::eek:stream &_ostr,
const Map1::value_type &_value
)
{
return _ostr << _value.first << ' ' << _value.second;
}

The type of Map1::value_type is int which means that you are trying to
overload the operator
std::eek:stream &operator<<(std::eek:stream&, int)
To overload an operator at least one of the operands needs to be of
user-defined type (which is why it works with the second one).
 
K

krzysztof.konopko

Erik Wikström napisa³(a):
The type of Map1::value_type is int which means that you are trying to
overload the operator
std::eek:stream &operator<<(std::eek:stream&, int)
To overload an operator at least one of the operands needs to be of
user-defined type (which is why it works with the second one).

No, 'Map1::value_type' is of type 'std::pair< const int, int >', not
'int', so I am trying to define function:
std::eek:stream &operator<<( std::eek:stream&, const std::pair< const int,
int > & )

If your statement would be true I got compilation error that
std::eek:stream &operator<<(std::eek:stream&, int) is already defined but I
didn't. My problem is that std::eek:stream_iterator< Map1::value_type >
cannot find my definition of operator<<().

If I write the code below everything is OK (it can find my definition
of operator<<() )
for ( Map1::const_iterator it = m_map1.begin(); it != m_map1.end(); it+
+ )
logfile << *it << std::endl;
 
S

Sylvester Hesp

I cannot compile the code which defines a std::map type consisting of
built in types and operator<< overload for std::map::value_type.
See the code below - I attach a full example.

Note: if I define map type with my new type (structure) everything is
OK. All compileres I've cheked report an error so I think it is a
problem with my code.

[.. snip ..]

class A
{
struct S // behavior similar to built-in int
{
int i;
S( int _i ) : i( _i ) {}
};

typedef std::map< int, int > Map1;
typedef std::map< int, S > Map2;

friend std::eek:stream &operator<<( std::eek:stream &_ostr, const
Map1::value_type &_value )
{
return _ostr << _value.first << ' ' << _value.second;
}

Try not to define the operator<<() inside A. While the definition of
operator<<() injects that function in the enclosing namespace, it is
still in the lexical scope of the class itself, and it will therefore
not be found during overload resolution. The reason that it works when
you use 'A' or 'S' as template arguments for the map, your class *is*
searched because of ADL, and the correct operator<<() is found.

- Sylvester
 
K

krzysztof.konopko

Sylvester Hesp napisa (a):
Try not to define the operator<<() inside A. While the definition of
operator<<() injects that function in the enclosing namespace, it is
still in the lexical scope of the class itself, and it will therefore
not be found during overload resolution. The reason that it works when
you use 'A' or 'S' as template arguments for the map, your class *is*
searched because of ADL, and the correct operator<<() is found.

- Sylvester

Yes, I thought that ADL is reason in this case but I want to keep Map1
typedef private in class A so it implies operator<<() to be a friend
of class A.
Moreover, I need to declare operator<<() in std namespace.

Of course, there are work-arounds:
- use loop instead of std::copy and std::eek:stream_iterator
- make Map1 typedef public and define operator<<() in std namespace

I prefer first solution but it fits only the simple example I
provided. The main problem is still unresolved so I think I need to
dig in and understand ADL deeper and find a solution.

Thanks
 
S

Sylvester Hesp

Sylvester Hesp napisa (a):

Yes, I thought that ADL is reason in this case but I want to keep Map1
typedef private in class A so it implies operator<<() to be a friend
of class A.
Moreover, I need to declare operator<<() in std namespace.

Yes indeed, I didn't even think of that.
Of course, there are work-arounds:
- use loop instead of std::copy and std::eek:stream_iterator
- make Map1 typedef public and define operator<<() in std namespace

And obviously:
- Use a user defined type that behaves like another (built-in) type,
like your 'A::S'.

I think I personally prefer that solution - you don't have to put
anything into the std namespace and you don't impose certain standard
unspecified behaviour with the standard types. What if another
programmer in your project tries to do something similar to what you're
doing? That is impossible because you have already put a definition for
operator<<(std::map<int,int>) in the std namespace.

By using a custom type you keep things private to your class.

- Sylvester
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top