Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't the compiler take the namespace name when the inner scope ones don't work?

I thought I understood name lookup very well (after having watched several videos about it and read a lot) but I just hit this case:

#include <iostream>

namespace test{

  struct Id
  {};

  void do_something( const Id& ){  std::cout << "Hello, World!" << std::endl; }

  class Test
  {
  public:

    void do_something() { std::cout << "WTF!" << std::endl;  }

    void run()
    {
      Id id;
      do_something( id ); // doesn't compile
    }

  };

}

int main()
{
    test::Test my_test;
  my_test.run();
}

The pointed line don't compile (on GCC4.8 and VC11U2) because it tries to use the member function test::Test::do_something() instead of the namespace-scoped test::do_something( const Id& ) which seem like the only possible candidate.

Apparently the member function name hides the namespace-scoped names which is surprising to me because I remember using almost similar code in other context without this problem spawning (but the conditions might have be very different in the end).

My question is: are these compilers confirming to the standard?

(Name lookup is very hard to understand by reading the standard document unfortunately, so I need expert confirmations)

like image 284
Klaim Avatar asked May 22 '13 16:05

Klaim


People also ask

Why do we use unnamed namespace?

An anonymous namespace makes the enclosed variables, functions, classes, etc. available only inside that file. In your example it's a way to avoid global variables. There is no runtime or compile time performance difference.

Why should you never place a using namespace directive inside of a header file?

The reason is that using directive eliminate the protection of that particular namespace, and the effect last until the end of current compilation unit.

What if namespace is not used?

Namespaces are basically just to organize your code. using namespace std; is used to inform the compiler to look for the io functions in this standard library. If it is not used, the compilation will flag an error that cin/cout is undefined. It is compiler dependent.

Why namespace is not used in C?

The problem with putting using namespace in the header files of your classes is that it forces anyone who wants to use your classes (by including your header files) to also be 'using' (i.e. seeing everything in) those other namespaces. However, you may feel free to put a using statement in your (private) *. cpp files.


2 Answers

My question is: are these compilers confirming to the standard?

Yes. Before overload resolution decides which functions are viable candidates (which includes checking the number of parameters) the compiler first has to do name lookup, to find all candidates, viable and non-viable. In your example name lookup stops after finding the member do_something() so overload resolution never gets a chance to decide whether the namespace-scope one is viable.

3.4.1 [basic.lookup.unqual]/1: "In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name."

3.4.1 [basic.lookup.unqual] paragraph 8 lists the contexts searched for the name and even has an example that answers your question exactly. The scope of Test is searched before the enclosing namespace, and as paragraph 1 says "name lookup ends as soon as a declaration is found for the name".

like image 116
Jonathan Wakely Avatar answered Oct 16 '22 16:10

Jonathan Wakely


The further a language goes out of its way to find a valid interpretation for a construct, the more likely it is that a typo or other such mistake will result in the compiler finding a meaning which is valid but wrong. The compiler is assuming that if foo is defined within some scope, and code within that scope uses foo, the programmer intends for the code to use the foo that is defined within the scope. If the programmer tries to do something with foo that is not permitted by its inner scope definition, odds are very good that one of the following is true:

  • The programmer meant to do some other slightly different operation, which would have been valid on the inner foo. A compiler could not be expected to know what the programmer intends unless the programmer specifies it.
  • The programmer meant to do the indicated operation, not realizing that the inner foo can't support it, and will thus have to find some other operation or sequence of operations the inner foo can support. Again, a compiler can't be expected to generate good code unless the programmer indicates how to use foo properly.
  • The programmer meant to do the operation in question on the outer foo, but declined to explicitly say so. If the compiler wanted to guess that this is what the programmer meant, it could generate code which would behave this way.

Only if the programmer's intention was #3 could a compiler possibly generate code which would behave as intended. It's far more likely, however, that a programmer really intended #1 or #2. If the compiler refuses to compile code even when assuming #3 would produce valid code, then any of the above mistakes will be found and can thus be corrected. By contrast, if compiler assumed #3 whenever it could, then if the programmer really intended #1 or #2 problems would not manifest themselves until the code was run and behaved contrary to design.

BTW, if I had my druthers, I would apply this principle to case-sensitivity in .NET languages, forbidding not only the writing of any identifier in a fashion inconsistent with the definition (as is done by C# but not vb.net), but the use of any identifier which differs only in upper/lower-casing from one in an inner scope. For example:

class foo
{
  int x;
  void bar()
  {
    int X=2;
    x=4; // ****
    return X;
  }
}

Given the above code, C# would guess that the line with the asterisks was intended to write the field; given similar code, vb.net would assume it was intended to write the local variable. Personally, I dislike both assumptions; the principle of "say exactly what you mean" would suggest to me that a compiler should require the programmer to either say this.x=4; or X=4;, neither of which could possibly be read as having the wrong meaning.

like image 27
supercat Avatar answered Oct 16 '22 18:10

supercat