Question

I have a Value class which can hold a value of various data types.

class Value
{
    private:
        union
        {
            int Integer;
            double Real;
            bool Boolean;
        };

        ValueTypes valType;

    public:
        Value();
        Value(double Val);
        Value(int Val);
        Value(bool Val);

        /* ... */

        friend std::ostream& operator <<(std::ostream& stream, const Value& val);
}

I would like to overload the << operator, so a value could be printed without the trouble of investigating its type.

This is my current implementation:

std::ostream &operator <<(std::ostream &os, const Value &val)
{
    switch (val.valType)
    {
        case ValueTypes::Real:
            os << val.Real;
            break;

        case ValueTypes::Integer:
            os << val.Integer;
            break;

        case ValueTypes::Boolean:
            os << (val.Boolean ? "True" : "False");
            break;
    }

    return os;
}

First, when compiling with g++ (CodeBlocks IDE, Ubuntu) the implementation causes an implicit conversion to Value. When the value type is either Integer or Real, the os << statement casts them to Value, which then calls to the ostream overload all over again (infinite recursion), so I end up with a segmentation error.

Second, when compiling with Visual C++, the implicit conversion is gone, and it prints the values just fine (Does not call the implicit constructor).

Notes:

I desire to keep the implicit constructors, since it enhances readability and eases maintenance in other sections of my project.

In addition, I found the following blog post, but failed to adjust my code so it will execute as I wish it to (The use of templates was a bit beyond my current understanding of cpp's syntax).

What can I do to disable implicit conversions in the specific ostream overloading function?

EDIT: With -Wall enabled I get no warnings in CodeBlocks.

Was it helpful?

Solution

You need to #include <ostream>. The reason it doesn't work without that is because that's the header that actually defines std::ostream and all those useful operator<< functions.

The reason it doesn't freak out and complain about "no definition for std::ostream" (or other the << operators) is because of forward declaration. If you forward declare a type, you can use it as a pointer or reference (so long as you don't try to further access it). You can do some things with an incomplete type.

Those other headers you were including were likely forward declaring std::ostream, but never giving a full definition for it and all the << operators. At least on your Ubuntu system. On the other systems, it's possible the other headers could have included <ostream> or at least provided some of the definitions that <ostream> does. Your operator<< was probably the only one the compiler was seeing (because you hadn't included <ostream>), and since it was the only one the compiler knew of, it was trying to use it for everything.

In short, when working on cross platform stuff, one should be pedantic about including exactly what's necessary (and not relying on other headers to include things that aren't related to them).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top