3

I write a class Fraction, and would like to display Fraction object with std::cout:

Fraction f(3, 5);
std::cout << f << std::endl;

I know overloading operator << can be used for this display purpose. But I can also overload operator type() function, and when only with operator double()(let type be double), I can still std::cout << f << std::endl.

Even more, I can also implement operator std::string(). But, when the mentioned 3 overloaded operator functions all exist, without explicitly type conversion, which one is called when doing std::cout << f << std::endl ? This really makes me curious, is this undefined behavior denpending on compiler implementation, or if there is some rule for scoring the closest/most suitable function to call?

To reproduce, use the following code:

#include <iostream>
#include <string>

class Fraction
{
public:
    Fraction(int num, int den=1):
        numerator(num), denominator(den) {}
    
    operator double() const {
        std::cout << "[operator double()]";
        return numerator*1.0 / denominator;
    }

    operator std::string() const {
        std::cout << "[operator std::string()]";
        return std::to_string(numerator) + "/" + std::to_string(denominator);
    }

private:
    int numerator;
    int denominator;

#ifdef OVERLOAD_STREAM_OP
    friend std::ostream& operator << (std::ostream& os, const Fraction& frac);
#endif

};

#ifdef OVERLOAD_STREAM_OP
std::ostream& operator << (std::ostream& os, const Fraction& frac)
{
    std::cout << "[operator <<]";
    os << std::to_string(frac.numerator) << "/" << std::to_string(frac.denominator);
    return os;
}
#endif

int main()
{
    Fraction f(3, 5);
    double d = 4 + f;
    std::cout << "\n--- now let's print\n";
    std::cout << "f: " << f << std::endl;
    std::cout << "f: " << std::string(f) << std::endl;
    std::cout << d << std::endl;

    return 0;
}

The output on my ubuntu 20.04:

(base) zz@home% clang++ fraction.cpp
(base) zz@home% ./a.out 
[operator double()]
--- now let's print
f: [operator double()]0.6
f: [operator std::string()]3/5
4.6
ChrisZZ
  • 1,521
  • 2
  • 17
  • 24
  • 2
    If your class provides both an `operator double()` and an `operator std::string()`, the usage `std::cout << f` is a diagnosable error due to ambiguity, since both conversions of `f` are equally viable. This doesn't apply to `4 + f` since there is no implicit conversion of `std::string` to `int` and no conversion of `int` to `std::string` but both operands `4` and `f` may be implicitly converted to `double` (and then added). The basic rule is that only one implicit conversion of any operand is permitted - and it is a diagnosable error if more than one conversion of any operand is possible. – Peter Jun 05 '21 at 05:05
  • @Peter I've tried again, if only provides `operator std::string( )`, then `std::cout << f` is not viable (at least with clang). – ChrisZZ Jun 05 '21 at 13:25
  • @ChrisZZ, Part of this strikes me as quite similar to a question I asked some time ago: https://stackoverflow.com/questions/17539555/why-does-outputting-a-class-with-a-conversion-operator-not-work-for-stdstring – chris Jun 05 '21 at 15:33
  • @chris: I’d tend to say it’s a duplicate of that question, in that that answer explains this behavior (aside from some minor bits about `+`). – Davis Herring Jun 05 '21 at 16:21

0 Answers0