Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interesting behavior of compiler with namespaces

Assume the following code:

#include <iostream>
using namespace std;

namespace X
{
  class A{};

  void f(A a){}

  void g(int a){}
}

int main()
{
  X::A a;
  f(a);
  g(5);
}

When I compile the code, the following compile error occurs:

main.cpp: In function 'int main()':
main.cpp: error: 'g' was not declared in this scope

So the function f is compiled perfectly, but g isn't. How? Both of them belong to the same namespace. Does the compiler deduce that function f belongs to the X namespace from the argument of type X::A? How does compiler behave in such cases?

like image 483
Eduard Rostomyan Avatar asked Sep 22 '14 14:09

Eduard Rostomyan


People also ask

What is the benefits of using namespace?

Advantages of namespace In one program, namespace can help define different scopes to provide scope to different identifiers declared within them. By using namespace - the same variable names may be reused in a different program.

What is the main purpose of the namespace?

A namespace is a declarative region that provides a scope to the identifiers (the names of types, functions, variables, etc) inside it. Namespaces are used to organize code into logical groups and to prevent name collisions that can occur especially when your code base includes multiple libraries.

Is it good practice to use namespace?

The statement using namespace std is generally considered bad practice. The alternative to this statement is to specify the namespace to which the identifier belongs using the scope operator(::) each time we declare a type.

Does using namespace std affect performance?

It doesn't affect the runtime performance at all.


3 Answers

X::A a; f(a); 

works because of Argument-Dependent Lookup (Also known as Koenig Lookup). a is an object of class A inside namespace X, when compiler searches a match-able function f, it will look into namespace X in this case. See Argument Dependent Lookup for more information.

like image 164
taocp Avatar answered Sep 22 '22 14:09

taocp


This works for the function call expression:

f(a);

because the namespace that X::A belongs to is included in the lookup for the function f due to argument dependent lookup(ADL), cppreference explains ADL as follows:

Argument-dependent lookup, also known as ADL, or Koenig lookup, is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.

Argument-dependent lookup makes it possible to use operators defined in a different namespace

This is covered in the draft C++ standard section 3.4.2 Argument-dependent name lookup:

When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found

and goes on to say:

For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument).

and includes the following bullet:

If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces of which its associated classes are members.[...]

and further down provides a similar example to your problem:

namespace NS {
  class T { };
  void f(T);
  void g(T, int);
}

NS::T parm;
void g(NS::T, float);

int main() {
  f(parm); // OK: calls NS::f
  extern void g(NS::T, float);
  g(parm, 1); // OK: calls g(NS::T, float)
}

The function call expression:

g(5);

does not work because ADL does not add any namespaces for arguments that are fundamental types.

Herb Sutter covers ADL in Gotw #30 and in What's In a Class? - The Interface Principle.

like image 34
Shafik Yaghmour Avatar answered Sep 25 '22 14:09

Shafik Yaghmour


When the code f(a), the compiler finds the function void f(A a){} in the namespace X because of the ADL (argument dependent lookup, also known as Koenig lookup).

A is declared in the namespace X, hence when the compiler needs to look up the definition of f, it includes possibilities from that namespace because the object a of type A is in that namespace (as declared X::A a;).

On the other hand, int is not declared in the namespace X, so the namespace X is not included in the lookup. Since no corresponding function for f is found, it fails to compile.

like image 27
Niall Avatar answered Sep 23 '22 14:09

Niall