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?
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.
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.
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.
It doesn't affect the runtime performance at all.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With