Reputation: 1187
I have an enum class of types and want a "to_string" function for outputting the type name so I wrote that inside my own namespace. Problem is, other functions in that namespace trying to call to_string on, e.g., an int (really just an int, not intended to be part of the enum) are finding the custom to_string and giving errors about invalid initialization of the enum.
I know I could explicitly call std::to_string instead of to_string but I assume there's a better way. What am I doing wrong?
Here is example code:
#include <iostream>
#include <string>
namespace other {
enum class Type {
Type1,
Type2
};
std::string to_string(const Type& type) {
switch(type) {
case Type::Type1:
return "Type1";
break;
case Type::Type2:
return "Type2";
break;
default:
{}
}
return "Unknown";
}
void run() {
using namespace std;
cout << string("Type: ") + to_string(Type::Type1) << endl;
cout << string("int: " ) + to_string(42) << endl; // this one generates compile-time errors
}
}
int main() {
other::run();
using namespace std;
cout << string("int: " ) + to_string(42) << endl; // This one is ok
return 0;
}
Upvotes: 3
Views: 159
Reputation: 64308
This is a tricky situation which involves some subtle rules of namespaces. Let's consider a simpler example:
namespace b {
void f(int) { }
}
namespace a {
using namespace b;
void f(char) { }
void g()
{
f(5); // calls f(char)
}
}
The issue here is that even though we have using namespace b
, the declarations inside b are treated as if they were declared in the common namespace (global) for the purposes of lookup:
(C++14 7.3.4/2)
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (3.4.1), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace. [ Note: In this context, “contains” means “contains directly or indirectly”. — end note ]
Because of this, for the purposes of lookup, the names in namespace b are treated as if they were in the global namespace. That means f(char)
inside namespace a will hide f(int)
inside namespace b:
(C++14 3.3.10/4)
During the lookup of a name qualified by a namespace name, declarations that would otherwise be made visible by a using-directive can be hidden by declarations with the same name in the namespace containing the using-directive; see (3.4.3.2).
In your example, a call to to_string(42)
in other::run()
will find other::to_string
, because std::to_string(int)
is hidden.
Upvotes: 3
Reputation: 93274
You need to explicitly specify what function you want to bring into the "overload set" (see wandbox example):
void run() {
using namespace std;
using std::to_string;
cout << string("Type: ") + to_string(Type::Type1) << endl;
cout << string("int: " ) + to_string(42) << endl;
}
The reason is that ADL ignores using directives. Refer to 3.4.2 [basic.lookup.argdep]:
When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2) except that: — Any using-directives in the associated namespace are ignored.
More detailed information available in this question.
Upvotes: 4