Reputation:
[EDIT - I long since forgot this was here until I got the 2,500 views "notable question". Since people are viewing - there is useful information about overloads in the accepted answer, but specifically checking for std::endl
is even worse than I realized at the time, and definitely the wrong thing.
Basically, the effect of std::endl
is to output \n
to the stream then flush with std::flush
. That's irrespective of platform, including Windows where the end-of-line is really "\r\n". The endl
manipulator doesn't abstract away platform differences WRT line ends, C++ handles that the same way that C does - by translating \n
to "\r\n" (for text mode, not binary) later on. I thought C++ was doing something different, an assumption so strong I never even questioned it for 2 decades, but I was wrong.
I don't remember details, but it's possible to define your own streams anyway, and provide alternative output (and translation) of whatever characters are streamed in. All manipulators should work as expected, before your custom stream code sees the resulting output characters. So to provide special end-of-line behaviour, watch for the \n
there (which is still before the text-file end-of-line translation).
]
It's hackish, I know, but I recently needed to implement a stream class which would act mostly like a standard stream, but which would detect the std::endl manipulator and special-case it's behaviour. My first attempt at a particular method implementation was...
mystream& mystream::operator<< (std::basic_ostream<char>& (*p) (std::basic_ostream<char>&))
{
if (p == &std::endl)
{
// Handle special case
}
else
{
m_Underlying_Stream << p;
}
return *this;
}
The trouble with this is that the compiler doesn't know which overload of std::endl
I'm referring to. I resolved it as follows...
mystream& mystream::operator<< (std::basic_ostream<char>& (*p) (std::basic_ostream<char>&))
{
typedef std::basic_ostream<char>& (*ENDL_T) (std::basic_ostream<char>&);
const ENDL_T l_ENDL (&std::endl);
if (p == l_ENDL)
{
// Handle special case
}
else
{
m_Underlying_Stream << p;
}
return *this;
}
That is the compiler can resolve the overload in the context of an initialisation (and for assignment too, as another experiment proved), but not for operator==
.
The compiler in question is MinGW GCC 4.4.0, but I don't think this is likely to be a compiler issue.
I had a look around and found this question...
How to get the address of an overloaded member function?
If my code has a const issue, I don't know where the missing const needs to go. I can't see any other obvious type issue.
I have some vague ideas about number-of-steps issues WRT overloading or implicit casting, but nothing concrete. So - can anyone explain clearly what is wrong with my first example, why the second version fixes it, and how I can safely indicate which overload I mean when taking the address of a function.
BTW - I can guess some people won't like me testing directly for the address of std::endl
, and will point out that this is fragile - e.g. someone could have their own manipulator which calls std::endl
which I wouldn't spot. In general this is true, but in this special case, the hack saves a lot of time and the nastiness just doesn't matter.
Upvotes: 0
Views: 3048
Reputation: 791889
The use of an overloaded function name, (or the name of a function template which behaves like a set of overloaded functions) without arguments (such as in an "address of" expression) is only allowed in a limited set of contexts where the context can be used to uniquely determine the particular overload required.
This is specified in 13.4 of the standard (ISO/IEC 14882:2003) [over.over]. Included are an initializer for an object or reference or in an explicit conversion. This gives you a number of options.
E.g. explicit conversion:
typedef std::ostream& (*ManipPtr)(std::ostream&);
mystream& mystream::operator<<(ManipPtr p)
{
if (p == static_cast<ManipPtr>(&std::endl))
{
// ...
Directly initializing a pointer:
typedef std::ostream& (*ManipPtr)(std::ostream&);
mystream& mystream::operator<<(ManipPtr p)
{
const ManipPtr pEndl = &std::endl;
if (p == pEndl)
{
// ...
Upvotes: 2
Reputation: 47428
The following works:
#include <iostream>
struct mystream {
typedef std::basic_ostream<char>& (*ENDL_T) (std::basic_ostream<char>&);
mystream& operator<< (ENDL_T p)
{
if (p == (ENDL_T)std::endl)
{
std::cout << "special case\n";
}
else
{
std::cout << "usual case\n";
}
return *this;
}
};
int main()
{
mystream ms;
ms << std::endl; // prints "special case"
ms << std::flush; // prints "usual case"
}
Upvotes: 1
Reputation: 14561
The reason it can't distinguish the overloads is because you're resolving the address of the function, not calling it. When you insert it in the stream, the compiler knows to call the endl(ostream&)
overload. Other than that, you're on your own.
Why not just test for '\n' instead?
Upvotes: 0