This question is inspired by this one. Consider the code:
namespace ns {
template <typename T>
void swap(T& a, T& b) {
using namespace std;
swap(a, b);
}
}
After some test with GCC, I found that swap(a, b); resolves to
1) std::swap if T has overloaded std::swap (e.g., standard container types)
2) ns::swap otherwise, leading to infinite recursion.
So, it seems that the compiler will first try to find a match in the namespace ns. If a match is found, the search ends. But this is not the case when ADL comes in, in which case, std::swap is found anyway. The resolution process seems to be complicated.
I want to know the details of what is going on under the hood in the process of resolving the function call swap(a, b) in the above context. Reference to the standard would be appreciated.
The code in the OP is equivalent to this:
using std::swap; // only for name lookup inside ns::swap
namespace ns {
template <typename T>
void swap(T& a, T& b) {
swap(a, b);
}
}
Why? Because using-directives like using namespace std; have a very peculiar behaviour C++14 [namespace.udir]p2:
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, the names
appear as if they were declared in the nearest enclosing namespace
which contains both the using-directive and the nominated namespace.
The nearest enclosing namespace that contains both namespace std and the block scope of function ns::swap is the global namespace.
Using-declarations such as using std::swap; on the other hand really introduce names into the scope in which they appear, not in some enclosing scope.
The lookup of a function call expression such as swap(a, b) is called unqualified lookup. The identifier swap has not been qualified with any namespace or class name, as opposed to ns::swap, which has been qualified via ns::. Unqualified lookup for potential names of functions consists of two parts: pure unqualified lookup and argument-dependent lookup.
Pure unqualified lookup stops at the nearest enclosing scope that contains the name. In the OP's example, as illustrated by the equivalent transformation shown above, the nearest scope that contains a declaration of the name swap is the namespace ns. The global scope will not be searched, std::swap will not be found via pure unqualified lookup.
Argument-dependent lookup searches all scopes (here: only namespaces and classes) associated with the argument types. For class types, the namespace in which the class has been declared in is an associated scope. Types of the C++ Standard Library such as std::vector<int> are associated with namespace std, hence std::swap can be found via argument-dependent lookup for the expression swap(a, b) if T is a C++ Standard Library type. Similarly, your own class types allow finding a swap function in the namespaces they have been declared in:
namespace N2 {
class MyClass {};
void swap(MyClass&, MyClass&);
}
Therefore, if argument-dependent lookup does not find a better match than pure unqualified lookup, you'll end up calling ns::swap recursively.
The idea behind calling swap unqualified, that is, swap(a, b) instead of std::swap(a, b) is that functions found via argument-dependent lookup are assumed to be more specialized than std::swap. Specializing a function template such as std::swap for your own class template type is impossible (since partial function template specializations are forbidden), and you may not add custom overloads to namespace std. The generic version of std::swap is implemented typically as follows:
template<typename T>
void swap(T& a, T& b)
{
T tmp( move(a) );
a = move(b);
b = move(tmp);
}
This requires a move-construction plus two move-assignments, which might even fall back to copies. Therefore, you can provide a specialized swap function for your own types in the namespaces associated with those types. Your specialized version can make use of certain properties of, or private access to, your own types.
The most important piece of the standard is 7.3.4/2 (quoting C++14 n4140, emphasis mine):
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.
The using-directive is located inside a function in :: ns, and nominates :: std. This means that for the purpose of unqualified name lookup, the effect of this using-directive is that names in ::std behave as if they were declared in ::. Particularly, not as if they were in ::ns.
Because the unqualified name lookup begins inside a function in ::ns, it will search ::ns before looking into ::. And it finds ::ns::swap, so it ends there, without examining ::, where it would find ::std::swap brought in by the using-directive.
Related
Here is a similar question, but in this question it works, however, it fails in the following circumstance, why?
namespace A
{
int k;
}
namespace B
{
class test{};
void k(const test&){/*do something*/}
}
int main()
{
using namespace A;
k(B::test());//compile error
}
Error message is: "'A::k' cannot be used as a function" (gcc 6.3.0)
That is to say, the compiler does not try to do ADL and never find the void k(const test&) in namespace B
However, I think the ADL should work in such situation because the code above does not belong to the following circumstance:
quoted from cppref
First, the argument-dependent lookup is not considered if the lookup set produced by usual unqualified lookup contains any of the following:
1) a declaration of a class member
2) a declaration of a function at block scope (that's not a using-declaration)
3) any declaration that is not a function or a function template (e.g. a function object or another variable whose name conflicts with the name of the function that's being looked up)
To be more precise, here the using namespace A does not introduce any declaration:
quoted from cppref
Using-directive does not add any names to the declarative region in which it appears (unlike the using-declaration), and thus does not prevent identical names from being declared.
The name lookup for a function call has two parts:
normal unqualified lookup
ADL
According to N4659 [basic.lookup.argdep]/3, the normal unqualified lookup happens first; and then ADL stage does not go ahead if the normal unqualified lookup found:
a declaration of a class member, or
a block-scope function declaration that is not a using-declaration, or
a declaration that is neither a function nor a function template.
In your code the normal unqualified lookup does find A::k as discussed in your previous question. So ADL does not happen for this code.
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 (6.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.
So, unqualified name lookup will find A::k, that is the reason for the error.
Here is the code example:
#include<iostream>
using namespace std;
namespace B
{
int ohoh=2;
}
namespace A
{
int ohoh=666;
namespace C
{
//using B::ohoh;(as if declared by using directive) //why does the lookup not stops here?
int foo()
{
using namespace B;
cout<<ohoh<<endl;
}
}
}
int main()
{
A::C::foo();
}
The output is 666 but not 2. Why?
Quoted from cppref
For an unqualified name, that is a name that does not appear to the right of a scope resolution operator ::, name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined. (Note: lookup from some contexts skips some declarations, for example, lookup of the name used to the left of :: ignores function, variable, and enumerator declarations, lookup of a name used as a base class specifier ignores all non-type declarations)
For the purpose of unqualified name lookup, all declarations from a namespace nominated by a using directive appear as if declared in the nearest enclosing namespace which contains, directly or indirectly, both the using-directive and the nominated namespace.
From the quoted paragraph above ,the name lookup should stop at nearest namespace C ,Where I've commented in the code.Why does it does not stop and find A::ohoh ?
By the way,I think I should use the using directive as little as possible.
For the purpose of unqualified name lookup, all declarations from a namespace nominated by a using directive as if declared in the nearest enclosing namespace which contains [...] both the using-directive and the nominated namespace.
In this case, the nearest namespace that contains both B and the using-directive is the global namespace. Therefore, all names from B appear inside A::C::foo as if they were declared in the global namespace. When searching for the name ohoh, A is searched before the global namespace, so A::ohoh is the first declaration found and name lookup stops there.
Consider the following clause in [namespace.memdef]/3:
If the name in a friend declaration is neither
qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost
enclosing namespace.
Is there a reason for the exception for template-id along with the qualified name? For that matter, is there a reason for the lookup of an unqualified name that isn't a template-id to be restricted to the innermost enclosing namespace? Is there a specific problem or use-case that this clause solves?
Why doesn't the restriction apply to qualified names and template-ids?
Qualified names and template-ids cannot introduce new members into the enclosing namespace, this is what the note in [namespace.memdef]p3 tries to say:
[ Note: The other forms of friend declarations cannot declare a new
member of the innermost enclosing namespace and thus follow the usual
lookup rules. — end note ]
Therefore, no such restriction is necessary for qualified names and template-ids.
Note that template-ids lack the declaration of the template-parameters, and qualified-ids could introduce names into distant, unrelated namespaces.
Why is there a restriction at all?
This part of the answer is still incomplete, but represents the current state of "research". Feel free to contribute.
The restriction has (probably?) been introduced due to N0783 - Namespace Issues and Proposed Resolutions which "attempts to clarify a number of namespace issues that are currently either undefined or incompletely specified".
This paper from 1995 contains two enlightening discussions of issues related to declarations of entities introduced via friend-declarations. Bear in mind that the name lookup rules back then were different:
Argument-dependent lookup had not yet been introduced(*)
Names introduced via friend-declarations are not found via pure unqualified lookup (no ADL) according to current rules, see [namespace.memdef]p3 and CWG 1477. The examples from N0878 suggest that those names could be found via pure unqualified lookup at that time.
(*) The best I could find was N0878 from March 1996, which says "A change was recently made to the working paper to add the “Koenig lookup rule”"
First, the example from N0783 for functions:
void f(char);
namespace A {
class B {
friend void f(char); // ::f(char) is a friend
friend void f(int); // A::f(int) is a friend
void bf();
};
void B::bf()
{
f(1); // calls A::f(int);
f('x'); // also calls A::f(int) because ::f is hidden
}
}
The second friend declaration must introduce a new function. N0783 tries to specify in which scope this declaration is introduced. It suggests
All friend declarations for a given name must declare entities in one
particular scope.
as a general rule, to avoid the surprises of situations such as the above.
So the question is, which scope do they declare entities in? There are
two possibilities, either
When looking for a previous declaration of the function, look until the nearest enclosing namespace is reached, or
When looking for a previous declaration, look in all enclosing scopes for the name of the function that was declared. If a previous
use of the name is found, the declaration is injected into that scope.
If no previous use of the name is found the friend is injected into
the nearest enclosing namespace scope.
Rule #2 would mean that the presence of any function called f in an
enclosing scope, whether or not the types match, would be enough to
cause a friend declaration to inject into that scope.
I believe that rule #2 is clearly unacceptable. A friend declaration
in a namespace would be affected by any global declaration of that
name. Consider what this would mean for operator functions! The
presence of any operator+ function in the global scope would force
all friend operator+ operators to appear in the global scope too!
The presence of a template in the global scope would have the same
effect.
For class types:
namespace N {
class A { void f(); };
}
using namespace N;
namespace M {
class B {
friend class A; // Without this rule
// makes N::A a friend
B();
};
class A { void f(); };
}
void N::A::f() { M::B b; } // A friend under current rules
void M::A::f() { M::B b; } // A friend under proposed rules
Both examples are not as interesting under the current rules because names introduced via friend declarations are only found via ADL. It is possible this restriction is a historical artefact. More "research" is required to follow the development of this restriction after the introduction of ADL.
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?
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.
I am studying this fascinating answer to a subtle question regarding the best practice to implement the swap function for user-defined types. (My question was initially motivated by a discussion of the illegality of adding types to namespace std.)
I will not re-print the code snippet from the above-linked answer here.
Instead, I would like to understand the answer.
The answer I've linked above states, beneath the first code snippet, in regards to overloading swap in namespace std (rather than specializing it in that namespace):
If your compiler prints out something different then it is not
correctly implementing "two-phase lookup" for templates.
The answer then goes on to point out that specializing swap in namespace std (as opposed to overloading it) produces a different result (the desired result in the case of specialization).
However, the answer proceeds with an additional case: specializing swap for a user-defined template class - in which case, again, the desired result is not achieved.
Unfortunately, the answer simply states the facts; it does not explain why.
Can someone please elaborate on that answer, and describe the process of lookup in the two specific code snippets provided in that answer:
overloading swap in namespace std for a user-defined non-template class (as in the first code snippet of the linked answer)
specializing swap in namespace std for a user-defined template class (as in the final code snippet of the linked answer)
In both cases, the generic std::swap is called, rather than the user-defined swap. Why?
(This will shed light on the nature of two-phase lookup, and the reason for the best practice for implementing user-defined swap; thanks.)
Preamble with plenty of Standardese
The call to swap() in the example entails a dependent name because its arguments begin[0] and begin[1] depend on the template parameter T of the surrounding algorithm() function template. Two-phase name lookup for such dependent names is defined in the Standard as follows:
14.6.4.2 Candidate functions [temp.dep.candidate]
1 For a function call where the postfix-expression is a dependent name,
the candidate functions are found using the usual lookup rules (3.4.1,
3.4.2) except that:
— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition
context are found.
— For the part of the lookup using associated
namespaces (3.4.2), only function declarations found in either the
template definition context or the template instantiation context are
found.
Unqualified lookup is defined by
3.4.1 Unqualified name lookup [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. If no
declaration is found, the program is ill-formed.
and argument-dependent lookup (ADL) as
3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
1 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. These modifications to the
search depend on the types of the arguments (and for template template
arguments, the namespace of the template argument).
Applying the Standard to the example
The first example calls exp::swap(). This is not a dependent name and does not require two-phase name lookup. Because the call to swap is qualified, ordinary lookup takes place which finds only the generic swap(T&, T&) function template.
The second example (what #HowardHinnant calls "the modern solution") calls swap() and also has an overload swap(A&, A&) in the same namespace as where class A lives (the global namespace in this case). Because the call to swap is unqualified, both ordinary lookup and ADL take place at the point of definition (again only finding the generic swap(T&, T&)) but another ADL takes place at the point of instantiation (i.e where exp::algorithm() is being called in main()) and this picks up swap(A&, A&) which is a better match during overload resolution.
So far so good. Now for the encore: the third example calls swap() and has a specialization template<> swap(A&, A&) inside namespace exp. The lookup is the same as in the second example, but now ADL does not pick up the template specialization because it is not in an associated namespace of class A. However, even though the specialization template<> swap(A&, A&) does not play a role during overload resolution, it is still instantiated at the point of use.
Finally, the fourth example calls swap() and has an overload template<class T> swap(A<T>&, A<T>&) inside namespace exp for template<class T> class A living in the global namespace. The lookup is the same as in the third example, and again ADL does not pick up the overload swap(A<T>&, A<T>&) because it is not in an associated namespace of the class template A<T>. And in this case, there is also no specialization that has to be instantiated at the point of use, so the generic swap(T&, T&) is being callled here.
Conclusion
Even though you are not allowed to add new overloads to namespace std, and only explicit specializations, it would not even work because of the various intricacies of two-phase name lookup.
It is impossible to overload swap in namespace std for a user defined type. Introduction an overload (as opposed to a specialization) in namespace std is undefined behavior (illegal under the standard, no diagnosis required).
It is impossible to specialize a function in general for a template class (as opposed to a template class instance -- ie, std::vector<int> is an instance, while std::vector<T> is the entire template class). What appears to be a specialization is actually an overload. So the first paragraph applies.
The best practice for implementing user-defined swap is to introduce a swap function or overload in the same namespace as your template or class lives in.
Then, if swap is called in the right context (using std::swap; swap(a,b);), which is how it is called in std library, ADL will kick in, and your overload will be found.
The other option is to do a full specialization of swap in std for your particular type. This is impossible (or impractical) for template classes, as you need to specialize for each and every instance of your template class that exists. For other classes, it is fragile, as specialization applies to only that particular type: subclasses will have to be respecialized in std as well.
In general, specialization of functions is extremely fragile, and you are better off introducing overrides. As you cannot introduce overrides into std, the only place they will be reliably found from is in your own namespace. Such overrides in your own namespace are preferred over overrides in std as well.
There are two ways to inject a swap into your namespace. Both work for this purpose:
namespace test {
struct A {};
struct B {};
void swap(A&, A&) { std::cout << "swap(A&,A&)\n"; }
struct C {
friend void swap(C&, C&) { std::cout << "swap(C&, C&)\n"; }
};
void bob() {
using std::swap;
test::A a, b;
swap(a,b);
test::B x, y;
swap(x, y);
C u, v;
swap(u, v);
}
}
void foo() {
using std::swap;
test::A a, b;
swap(a,b);
test::B x, y;
swap(x, y);
test::C u, v;
swap(u, v);
test::bob();
}
int main() {
foo();
return 0;
}
the first is to inject it into the namespace directly, the second is to include it as an inline friend. The inline friend for "external operators" is a common pattern that basically means you can only find swap via ADL, but in this particular context does not add much.