I sent this to the gcc mailing list and it was pointed out that I should have sent it here, so I am forwarding it here in hopes that someone can shed some light on this. Is this a bug in g++ or is this behavior mandated by the standard? Why would the conversion from bool(0) -> NULL -> string() be better than Variant(bool)? It seems to me that the selection of which function to use for false should be the same as for true. This also happens with int, if the argument is not 0 it chooses the Variant function. BTW, the code is separated for strings for a reason, this is stripped down code from KDE and strings are possibly translated, everything else isn't, so strings must be treated differently. ---------- Forwarded Message ---------- Subject: zero and pointer conversions in g++ Date: Monday 26 December 2005 14:04 From: Thomas Braxton <brax108@xxxxxxx> To: gcc@xxxxxxxxxxx I have this test code that I think g++ is selecting the wrong function when the second argument is a zero. If compiled with HAVE_ASCII_DEFAULT 1 it selects read(const char*, const char*) instead of read(const char*, const Variant&), for test2a/test3a. If compiled with HAVE_ASCII_DEFAULT 0 compilation fails because test2a/test3a are ambiguous. Shouldn't all of the tests select the Variant function, or am I missing something? Why do I have to explicitly create a Variant when the argument is zero? Tested w/ g++ 3.4.3 (Mandrakelinux 10.2 3.4.3-7mdk) and 4.0.1 (self compiled) TIA, Thomas PS please CC as I'm not subscribed -------------------------------------------------------
#include <iostream> #include <string> #define HAVE_ASCII_DEFAULT 1 using std::string; using std::ostream; using std::cout; using std::endl; class Variant { public: enum Type { Invalid, Bool, Int, String }; Variant() : i(), b(), s(), type(Invalid) { ; } Variant(Type t) : i(), b(), s(), type(t) { ; } Variant(bool b) : i(), b(b), s(), type(Bool) { ; } Variant(int i) : i(i), b(), s(), type(Int) { ; } Variant(const string& s) : i(), b(), s(s), type(String) { ; } const char* type_name() const; friend ostream& operator<<(ostream& os, const Variant& v); private: int i; int b; string s; Type type; }; const char* Variant::type_name() const { switch(type) { case Bool: return "Bool"; case Int: return "Int"; case String: return "String"; default: break; } return "Invalid"; } ostream& operator<<(ostream& os, const Variant& v) { os << "Variant(" << v.type_name(); switch(v.type) { case Variant::Bool: os << ", " << (v.b? "true": "false"); break; case Variant::Int: os << ", " << v.i; break; case Variant::String: os << ", \"" << v.s.c_str() << '\"'; break; default: break; } return os << ')'; } class Config { public: Config() { ; } string read(const char* pKey, const string& aDefault) const; #if HAVE_ASCII_DEFAULT string read(const char* pKey, const char* aDefault) const; #endif Variant read(const char* pKey, const Variant& aDefault) const; }; #if HAVE_ASCII_DEFAULT string Config::read( const char *pKey, const char *aDefault ) const { if (!aDefault) return read(pKey, string()); return read(pKey, string(aDefault)); } #endif string Config::read( const char *pKey, const string& aDefault ) const { cout << __PRETTY_FUNCTION__ << '\t' << (pKey && *pKey? pKey: "null") << endl << "\taDefault = \"" << aDefault.c_str() << '"' << endl; return aDefault; } Variant Config::read( const char *pKey, const Variant &aDefault ) const { cout << __PRETTY_FUNCTION__ << '\t' << (pKey && *pKey? pKey: "null") << endl << "\taDefault = " << aDefault << endl; return aDefault; } int main(int, char**) { Config config; // bool config.read("test1", true); config.read("test2a", false); config.read("test2b", Variant(false)); config.read("test3a", bool()); config.read("test3b", Variant(bool())); return 0; }