4
#include <iostream>
#include <string>

class X {};

namespace N
{

std::string to_string(X)
{
    return "foo";
}

void foo()
{
    //using std::to_string; // will break the build if uncommented...
    //using N::to_string;   // ...unless this is uncommented as well
    std::cout << to_string(X()) << std::endl;
}

}

int main()
{
    N::foo();
    return 0;
}

Either I have stumbled upon one of the many C++ arcana I do not master, or I am missing something obvious here.

How can using std::to_string apparently reduce the set of names available during unqualified lookup to only those reachable via ADL? While I can guess the problem might be that to_string(X) is declared in a different namespace than X, I cannot help but notice that, without using std::to_string, N::to_string(X) is simply available to N::foo() by using the "normal", intuitive lookup rules I am accustomed to.

gd1
  • 11,300
  • 7
  • 49
  • 88

1 Answers1

5

This is not specific to a using-declaration, but follows normal scoping rules. When you introduce a name into the function, that hides equally named things from outer scopes.

You could just as well have an int to_string; local variable in foo. That would also hide N::to_string.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • I notice, however, that the analogy with declaring `int to_string` does not *always* hold: in fact, `using std::to_string` does not hide a `to_string(X)` function declared in `X`'s namespace to be found from ADL, whereas `int to_string` will cause a compiler error. The analogy holds if I declare a `std::string to_string(int)`, for example, within `foo()`. – gd1 Feb 07 '16 at 13:26
  • 1
    Yes, that's a different case. ADL will make the compiler consider a function from the parameter's associated namespace(s), even if it wouldn't be seen otherwise. But that's not the case for the code in the question, as `X` is outside of the namespace. – Bo Persson Feb 07 '16 at 16:55