1
votes

Note: Previously I haven't had this problem! My to_string method was working just fine, the template instantiation found the right overload for my Point class and I've been using it for months! However something went wrong with the last commit and in one source file I'm getting this well-known error message (only in this source file, in other places mx::to_string(mx::Point) works without errors). I was not able to figure out what could have been the reason, as I did not notice any line that could have potentially screwed up this.

Plus have swept through all the related questions regarding stream operator overloading without finding a solution.

So I have a point class:

#ifndef MXBASIC_UTILITIES_H
#define MXBASIC_UTILITIES_H
#include <ostream>
#include <string>

namespace mx{
    class Point;
}

class mx::Point
{
public:
    Point();
    Point(int x, int y);

    int getX() const;
    int getY() const;
    int& getX();
    int& getY();

    //...
};

inline mx::Point::Point() : _x(0), _y(0) {}
inline mx::Point::Point(int x, int y) : _x(x), _y(y) {}

inline int mx::Point::getX() const { return _x; }
inline int mx::Point::getY() const { return _y; }
inline int& mx::Point::getX() { return _x; }
inline int& mx::Point::getY() { return _y; }

// THE OVERLOAD:

inline std::ostream& operator<<(std::ostream& os, const mx::Point& point){
    return os << '(' << point.getX() << ',' << point.getY() << ')';
}

#endif

A helpertypes.h named header file where I have my to_string function:

#ifndef HELPERTYPES_H
#define HELPERTYPES_H
#include <iostream>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string>

// ...

namespace mx{
template<typename T>
std::string to_string(const T& val)
{
    std::ostringstream os;

    os.flags(std::ios::fixed);
    os.precision(2);

    os << val; // getting error here as of recently, both in MinGW 4.7.3 and MSVC15

    return os.str();
}
}
#endif

Full error messages: More readable here: pastebin.com/gFdFezrV

MSVC15:

2>c:(...)\classes\helpertypes.h(87): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'const mx::Point' (or there is no acceptable conversion) 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(495): note: could be 'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_streambuf> *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(475): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(const void *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(455): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(long double)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(435): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(double)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(415): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(float)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(395): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned __int64)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(375): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(__int64)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(355): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned long)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(335): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(long)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(315): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned int)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(290): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(int)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(270): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned short)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(236): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(short)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(216): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(bool)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(209): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(std::ios_base &(__cdecl *)(std::ios_base &))' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(202): note: or
'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_ios> &(__cdecl *)(std::basic_ios> &))' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(196): note: or 'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_ostream> &(__cdecl *)(std::basic_ostream> &))' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(692): note: or 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(739): note: or 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,char)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(777): note: or 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(824): note: or 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,char)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(950): note: or 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const signed char *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(957): note: or
'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,signed char)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(964): note: or 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const unsigned char *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(971): note: or
'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,unsigned char)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(981): note: or 'std::basic_ostream> &std::operator <<,mx::Point>(std::basic_ostream> &&,const _Ty &)' 2> with 2> [ 2>
_Ty=mx::Point 2> ] 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(1019): note: or
'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const std::error_code &)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\random(2568): note: or
'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const std::bernoulli_distribution &)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\thread(246): note: or
'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,std::thread::id)' 2> c:(...)\classes\helpertypes.h(87): note: while trying to match the argument list '(std::ostringstream, const mx::Point)' 2> c:(...)\classes(...)\entity\tinyent\tinyent.cpp(1502): note: see reference to function template instantiation 'std::string mx::to_string(const T &)' being compiled 2> with 2> [ 2> T=mx::Point 2> ]

MinGW 4.7.3:

In file included from jni/../../Classes/utilities/pattern/pat.h:5:0, from jni/../../Classes/(...)/entity/tinyent/../ent.h:3, from jni/../../Classes/(...)/entity/tinyent/tinyent.h:2, from jni/../../Classes/(...)/entity/tinyent/tinyent.cpp:1: jni/../../Classes/helpertypes.h: In instantiation of 'std::string mx::to_string(const T&) [with T = mx::Point; std::string = std::basic_string]': jni/../../Classes/(...)/entity/tinyent/tinyent.cpp:1502:20: required from here jni/../../Classes/helpertypes.h:87:5: error: cannot bind 'std::basic_ostream' lvalue to 'std::basic_ostream&&' os << val; ^ In file included from jni/../../Classes/utilities/pattern/abspat.h:6:0, from jni/../../Classes/utilities/pattern/pat.h:3, from jni/../../Classes/(...)/entity/tinyent/../ent.h:3, from jni/../../Classes/(...)/entity/tinyent/tinyent.h:2, from jni/../../Classes/(...)/entity/tinyent/tinyent.cpp:1: C:/Users/(...)/Documents/libs/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/ostream:602:5: note: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits; _Tp = mx::Point]' operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x) ^

Place where the instantiation's triggered:

tinyent.h

#pragma once
#include <string>
#include "ent.h"
#include "mxbasic_utilities.h" // definition of my overload included!

class TinyEnt : public Ent
{
public:
    // ...
    virtual std::string toString() const override;

private:
    // ...
    int _posX, posY;
};

tinyent.cpp

#include "tinyent.h"

// ...

std::string toString() const
{
    return std::string("TinyEnt ") + mx::to_string(id()) + " at " // decltype(id()) is ULL, no problem here
        + mx::to_string(mx::Point(_posX, _posY)); // place of instantiation, overload not found
}

If I put the definition of my to_string function right in front of this toString function it works:

alternate tinyent.cpp

#include "tinyent.h"


template<typename T>
std::string to_string(const T& val)
{
    std::ostringstream os;

    os.flags(std::ios::fixed);
    os.precision(2);

    os << val; 

    return os.str();
}

std::string toString() const
{
    return std::string("TinyEnt ") + mx::to_string(id()) + " at "
        + ::to_string(mx::Point(_posX, _posY)); // no problems this way
}

I have absolutely no idea what to do with this. I have checked the include hierarchy if there were maybe recursive inclusions, there aren't (helpertypes.h only depends on stl headers). I have tried rebuilding from ground 0, and tried using different compilers. Do you have any hint what could have gone wrong?

EDIT: When using full namespace qualifications in my mx::to_string function (like so: ::operator(os, val);) I get different error messages. The instantiations raise ambiguity errors when fed with size_t (unsigned int in this case), as if it wasn't found in the first round of argument lookup.

The error messages produced by MSVC15 and MinGW.

In file included from jni/../../Classes/utilities/pattern/pat.h:5:0, from jni/../../Classes/patmgr.h:4, from jni/../../Classes/patmgr.cpp:1: jni/../../Classes/helpertypes.h: In instantiation of 'std::string mx::to_string(const T&) [with T = unsigned int; std::string = std::basic_string]': jni/../../Classes/utilities/pattern/pat_bit_spec.h:185:37: required from here jni/../../Classes/helpertypes.h:94:22: error: call of overloaded 'operator<<(std::ostringstream&, const unsigned int&)' is ambiguous ::operator<<(os, val); ^ jni/../../Classes/helpertypes.h:94:22: note: candidates are: In file included from jni/../../Classes/utilities/pattern/pat.h:3:0, from jni/../../Classes/patmgr.h:4, from jni/../../Classes/patmgr.cpp:1: jni/../../Classes/utilities/pattern/abspat.h:72:22: note: std::ostream& operator<<(std::ostream&, AbsPat::Loc) inline std::ostream& operator<<(std::ostream& os, AbsPat::Loc loc) ^ jni/../../Classes/utilities/pattern/abspat.h:72:22: note: no known conversion for argument 2 from 'const unsigned int' to 'AbsPat::Loc'

In this segment: error: call of overloaded 'operator<<(std::ostringstream&, const unsigned int&)' is ambiguous ::operator<<(os, val); ^ How could this be ambiguous?

1
To establish the obvious: does your helpertypes.h (still) include the header file with your operator<< definition?Smeeheey
Also, you can try to change your line os << val to (fully namespace-qualified) ms::operator<<(os, val) to see if that's where the problem liesSmeeheey
helpertypes.h does not need to have the definition of the overloaded operator included, that's the instantiation's job. The fully-qualified namespace would be ::operator<<(os, val) as it's a global function, however I'm getting different error messages when using ::operator<<(os, val), looking into it..MatrixAndrew
Agreed that it does not need to see the definition, but it does need to see at least a declaration, which must occur earlier on in the compilation unit than the to_string() function in helpertypes.h. Try adding the line std::ostream& operator<<(std::ostream& os, const mx::Point& point); immediately above your to_string() declaration to confirm/disprove thisSmeeheey
I tried the forward declaration, didn't solve it sadly. And interestingly if I use the ::operator<<(os, val) I get ambiguous function calls for all the usages of mx::to_string, not sure how to interpret that.MatrixAndrew

1 Answers

0
votes

For some reason the compiler thought to itself it is not going to consider my overload defined in the global namespace anymore. I then tried defining the overload in the same namespace as a friend, but then I'd gotten these ambiguity errors. The ambiguity errors were raised because I left a forward declaration of my overload as if it were defined in the global namespace as opposed to the mx:: namespace. Once I removed the forward declaration and let the compiler utilize the power of ADL, for God's sake it compiled.

It's still a prolonging mystery for me why the lookup for the overload in the global namespace broke (and why only in one compilation unit).

Moral of the story: I'll only declare operator overloads in the same namespace where the class is from now on.