Home > Software engineering >  Ambiguity when inheriting ostringstream
Ambiguity when inheriting ostringstream

Time:10-21

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;
}

Demo.

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;
}
  •  Tags:  
  • c
  • Related