I have a simple struct which inherits from std::ostringstream
, in order to handle some values better for databases. If I just inherit, and add in a simple constructor to set widths for precision of doubles, it works fine.
struct myostream : std::ostringstream {
myostream() noexcept( false ) {
this->precision( 6 );
this->setf( ios_base::fixed, ios_base::floatfield );
}
};
Now, I'm trying to add in an operator<<
for type double
, in order to handle infinity, and NaN.
myostream &operator<<( double val ) {
if ( std::isnan( val ) ) {
*static_cast<std::ostringstream *>( this ) << "'NaN'";
} else if ( std::isinf( val ) ) {
*static_cast<std::ostringstream *>( this ) << ( val < 0 ? "'-Infinity'" : "'Infinity'" );
} else {
*static_cast<std::ostringstream *>( this ) << val;
}
return *this;
}
As soon as I add this function in, I just get ambiguous function calls everywhere, for every type (bool
, char
, char *
, int
, etc.).
2>...\include\myostream.hpp(13,14): message : could be 'myostream &myostream::operator <<(double)' (compiling source file ...)
2>C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.30.30704\include\ostream(694,32): message : or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)' (compiling source file ...)
2>C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.30.30704\include\ostream(775,31): message : or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)' (compiling source file ...)
2>C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.30.30704\include\ostream(856,32): message : or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,_Elem)'
2> with
2> [
2> _Elem=char
2> ] (compiling source file ...)
2>C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.30.30704\include\ostream(898,31): message : or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,signed char)' (compiling source file ...)
2>C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.30.30704\include\ostream(909,31): message : or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,unsigned char)' (compiling source file ...)
2>C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.30.30704\include\thread(275,26): message : or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,std::thread::id)' (compiling source file ...)
2>...: message : or 'built-in C operator<<(int, int)'
2>...: message : while trying to match the argument list '(myostream, char)'
So, my question is: how would I go about overriding operator<<
for some types, without adding in ambiguity? Is this even possible, or would I need to make a function which returns a string instead?
Edit:
Adding in using std::ostringstream::operator<<;
just increases the number of ambiguities.
CodePudding user response:
You can use SFINAE to prevent the implicit conversion of double
in myostream::operator<<
when T
is not a double
:
template<class T, std::enable_if_t<std::is_same_v<T, double>>* = nullptr>
myostream &operator<<( T val ) {
if ( std::isnan( val ) ) {
*static_cast<std::ostringstream *>( this ) << "'NaN'";
} else if ( std::isinf( val ) ) {
*static_cast<std::ostringstream *>( this ) << ( val < 0 ? "'-Infinity'" : "'Infinity'" );
} else {
*static_cast<std::ostringstream *>( this ) << val;
}
return *this;
}
CodePudding user response:
Here's one option:
struct myostream : std::ostringstream {
using std::ostringstream::operator<<; // bring in all the member overloads
myostream() noexcept( false ) {
this->precision( 6 );
this->setf( ios_base::fixed, ios_base::floatfield );
}
// call the base class member function explicitly in here:
myostream &operator<<( double val ) {
if ( std::isnan( val ) ) {
std::ostringstream::operator<<("'NaN'");
} else if ( std::isinf( val ) ) {
std::ostringstream::operator<<( val < 0 ? "'-Infinity'" : "'Infinity'" );
} else {
std::ostringstream::operator<<(val);
}
return *this;
}
};
// front-end for all the free operator<< functions:
// (this could be narrowed down to not match on T:s that isn't supported)
template<class T>
myostream& operator<<(myostream& m, const T& x) {
static_cast<std::ostringstream&>(m) << x;
return m;
}