anything wrong with conversion operator?

E

ES Kim

Consider:

#include <string>
#include <iostream>
using namespace std;

struct S
{
const char* ps_;
operator string();
};

int main()
{
S s;
cout << s;
}

operator<<(ostream&, const string&) is defined in the standard and
the conversion from S to string is, too. But compilers reject this code
since operator<<(ostream&, const S&) is not defined.
What's wrong with this code?
 
A

Alf P. Steinbach

* ES Kim:
Consider:

#include <string>
#include <iostream>
using namespace std;

struct S
{
const char* ps_;
operator string();
};

int main()
{
S s;
cout << s;
}

operator<<(ostream&, const string&) is defined in the standard and
the conversion from S to string is, too.

The Holy Standard says nothing about your struct S.

But compilers reject this code

As they should.

since operator<<(ostream&, const S&) is not defined.

If you're sure that's the reason...

What's wrong with this code?

.... then why are you asking this?

Is this homework?

Anyway, even with the code corrected (always post code that is complete,
if possible) MSVC 7.1 rejects it. It has no problem with conversion
to 'char const*', however. I don't know why, but one slight difference is
that in the MSVC implementation of the standard library the operator<< for
strings must infer template arguments such as the allocator type for the
string; why that should matter just baffles me, since both operator
implementations must infer template arguments for the stream.
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* ES Kim:

Anyway, even with the code corrected (always post code that is complete,
if possible) MSVC 7.1 rejects it. It has no problem with conversion
to 'char const*', however. I don't know why, but one slight difference is
that in the MSVC implementation of the standard library the operator<< for
strings must infer template arguments such as the allocator type for the
string; why that should matter just baffles me, since both operator
implementations must infer template arguments for the stream.

Adding the following to the mix (after class S, for example) works so it
seems to have to do with template argument deduction -- there is a rule
about just direct conversions being tried, but I don't recall exactly:

template<class Traits> inline
basic_ostream<char, Traits>& operator<<(
basic_ostream<char, Traits>& stream,
const basic_string<char>& s
)
{
return std::eek:perator<<( stream, s );
}
 
E

ES Kim

Alf P. Steinbach said:
The Holy Standard says nothing about your struct S.



As they should.



If you're sure that's the reason...



... then why are you asking this?

Is this homework?

I wish it were. I'm too old to do such a thing. ;-)
Anyway, even with the code corrected (always post code that is complete,
if possible) MSVC 7.1 rejects it. It has no problem with conversion
to 'char const*', however. I don't know why,

Neither do I, and that's why I asked the question.
Ok, let me clarify.

struct S
{
operator const char*();
};

S s;
cout << s;

If you define S like this, it's fine, and operator<<(ostream&, const char*)
is called. Why not with operator string()?
but one slight difference is
that in the MSVC implementation of the standard library the operator<< for
strings must infer template arguments such as the allocator type for the
string; why that should matter just baffles me, since both operator
implementations must infer template arguments for the stream.

The original code is rejected not just with MSVC but also with gcc and comeau.
So I think it's not just an implementation problem but language-related one.
 
E

ES Kim

Alf P. Steinbach said:
* Alf P. Steinbach:

... code snipped
Adding the following to the mix (after class S, for example) works so it
seems to have to do with template argument deduction -- there is a rule
about just direct conversions being tried, but I don't recall exactly:

template<class Traits> inline
basic_ostream<char, Traits>& operator<<(
basic_ostream<char, Traits>& stream,
const basic_string<char>& s
)
{
return std::eek:perator<<( stream, s );
}

Well, I don't think this is a solution. If this code and std::string-related
headers are included in a same source somehow, it will cause an ambiguity.
 
A

Alf P. Steinbach

* ES Kim:
Well, I don't think this is a solution. If this code and std::string-related
headers are included in a same source somehow, it will cause an ambiguity.

Well it doesn't with MSVC 7.1. And I don't think with any other compiler
either. It's a better match -- but perhaps one should take a look at
what operator<<'s the standard specifies (I'm too lazy to do that).
 
E

ES Kim

Alf P. Steinbach said:
* ES Kim:

Well it doesn't with MSVC 7.1. And I don't think with any other compiler
either.

Hmmm... that's strange. It does with gcc and comeau, not just with MSVC.
Here's my test code:

#include <iostream>
#include <string>

struct S
{
char* s;
operator string();
};

// your code
template<class Traits> inline
basic_ostream<char, Traits>& operator<<(
basic_ostream<char, Traits>& stream,
const basic_string<char>& s
)
{
return std::eek:perator<<( stream, s );
}

int main()
{
S s;
std::cout << s;
std::string str;
std::cout << str; // ambiguous
}
It's a better match -- but perhaps one should take a look at
what operator<<'s the standard specifies (I'm too lazy to do that).

This is from the standard:

template<class charT, class traits, class Allocator>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os,
const basic_string<charT, traits, Allocator>& str);

Does your code match better than this one? I'm not sure.
 
A

Alf P. Steinbach

* ES Kim:
Hmmm... that's strange. It does with gcc and comeau, not just with MSVC.
Here's my test code:

#include <iostream>
#include <string>

struct S
{
char* s;
operator string();
};

// your code
template<class Traits> inline
basic_ostream<char, Traits>& operator<<(
basic_ostream<char, Traits>& stream,
const basic_string<char>& s
)
{
return std::eek:perator<<( stream, s );
}

int main()
{
S s;
std::cout << s;
std::string str;
std::cout << str; // ambiguous
}
Ooops.



This is from the standard:

template<class charT, class traits, class Allocator>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os,
const basic_string<charT, traits, Allocator>& str);

Does your code match better than this one? I'm not sure.

I'm not sure either.

In such situations I apply the simple rule: don't rely on things that
are hard to understand or generally unknown.

So a 'toString' member function in class S is one solution (although it
would be nice to know the answer(s) to this question, also).
 
E

ES Kim

Alf P. Steinbach said:
So a 'toString' member function in class S is one solution (although it
would be nice to know the answer(s) to this question, also).

Yes, this is what I would do in real code. Someone asked the question
and I was just curious. No, not exactly. I'm dying to know the answer. :)
 
I

Ioannis Vranos

ES said:
Consider:

#include <string>
#include <iostream>
using namespace std;

struct S
{
const char* ps_;
operator string();
};

int main()
{
S s;
cout << s;
}

operator<<(ostream&, const string&) is defined in the standard and
the conversion from S to string is, too. But compilers reject this code
since operator<<(ostream&, const S&) is not defined.
What's wrong with this code?


The most serious error here is that you use operator definition for fun.
operator definitions can cause unexpected/hidden errors, so must be used
with extreme caution.


#include <string>
#include <iostream>



struct S
{
const char *ps_;
};


inline
std::eek:stream &operator<<(std::eek:stream &o, const S &s)
{
return std::cout<<s.ps_;
}



int main()
{
S s = {0};
std::cout << s;
}
 
I

Ioannis Vranos

Ioannis said:
The most serious error here is that you use operator definition for fun.
operator definitions can cause unexpected/hidden errors, so must be used
with extreme caution.


I was talking about operator Type() definitions. Avoid them.
 
E

ES Kim

Ioannis Vranos said:
I was talking about operator Type() definitions. Avoid them.

Yes, I know why the conversion operator must be used with caution
and agree on that. But I'm not talking about the recommended style.
What I'm asking is "Why the original code is rejected even if the
conversion is defined?" It's about syntax.
 
J

JKop

ES Kim posted:
Consider:

#include <string>
#include <iostream>
using namespace std;

struct S
{
const char* ps_;
operator string();
};

int main()
{
S s;
cout << s;
}

operator<<(ostream&, const string&) is defined in the standard and
the conversion from S to string is, too. But compilers reject this code
since operator<<(ostream&, const S&) is not defined.
What's wrong with this code?


int main()
{
k = 7;
}


That's one problem in anyway.


-JKop
 
P

Paul

ES Kim said:
Consider:

#include <string>
#include <iostream>
using namespace std;

struct S
{
const char* ps_;
operator string();
};

int main()
{
S s;
cout << s;
}

operator<<(ostream&, const string&) is defined in the standard and
the conversion from S to string is, too. But compilers reject this code
since operator<<(ostream&, const S&) is not defined.
What's wrong with this code?

With the code:
cout << s;
Why would the compiler look for an << operator in std::string?
It can't go looking into every possible conversion type for obvious reasons.

Paul.
 
R

Rob Williscroft

ES Kim wrote in in comp.lang.c++:
Consider:

#include <string>
#include <iostream>
using namespace std;

struct S
{
const char* ps_;
operator string();
};

int main()
{
S s;
cout << s;
}

operator<<(ostream&, const string&) is defined in the standard and
the conversion from S to string is, too. But compilers reject this code
since operator<<(ostream&, const S&) is not defined.
What's wrong with this code?

std::string is a typedef for std::basic_string< char > and operator <<
in std is a template and its second argument is involved in TAD (template
argument deduction), such template arguments must be exact matches.

The only (*) conversion's allowed (or possible if you like) are derived
to base conversion's, User defined convertion's (operator Type()
and converting constructors) aren't considered as they can play no
role in TAD.

*) All the normal rvalue/lvalue and qualification adjustment can
occur as usual.


An example:

#include <iostream>
#include <ostream>

/* Forward
*/
template < typename T > struct D;

template < typename T > struct A
{
A() {}
A( D< T > const & ) {};
};

template < typename T > struct B : A < T > {};

template < typename T > struct C
{
operator A< T > () const { return A< T >(); }
};

template < typename T > struct D {};


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

int main()
{
A< int > a;

f( a );

B< int > b;

f( b );

#if 0
C< int > c;

f( c ); /* Not Ok User defined conversion (operator) */
#endif

#if 0
D< int > d;

f( d ); /* Not Ok User defined conversion (constructor) */
#endif

}

HTH.

Rob.
 

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,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top