How does ADL affect this piece of C++ code?
In this case, normal name lookup finds N2::foo
, and N1::foo
is found by ADL, they're both added to the overload set, then overload resolution is performed and the calling is ambiguous.
BTW: Without using N2::foo;
in main()
, ::foo
will be found by normal name lookup, and N1::foo
is found by ADL too; as the result the calling is still ambiguous.
Updated:
So, the question here is why
::foo
can not be called by "foo(N1::S{});
" in themain
function?
Because with the usage of using N2::foo;
, the name N2::foo
is introduced in the main
function. When calling foo
the name N2::foo
will be found at the scope of main
, then name lookup stops, the further scope (the global namespace) won't be examined, so ::foo
won't be found and added to overload set at all. As the result N2::foo
is called for both cases.
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.
BTW: If you put using N2::foo;
in global namespace before main
, foo(N1::S{});
would call ::foo
. Both N2::foo
and ::foo
are found by name lookup and ::foo
wins in overload resolution.
LIVE
Confusion around function call resolution
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.
How does the using directive affect function arguments in C++?
The code is correct but it has nothing to do with argument dependent look-up. Also, the using declaration only affects finding of usesFoo
and not foo
: once you have uttered the name of a class member, other names are looked up in the context of this class. Since foo
is a member of test::usesFoo` it found. Without the using directive you'd need to define the member function like this:
void test::usesFoo::memberFunction(foo& input) {
(void)input;
}
The relevant clause for this is 3.4.1 Unqualified Name Look-up [basic.lookup.unqual] paragraph 6:
A name used in the definition of a function following the function’s declarator-id that is a member of namespace N (where, only for the purpose of exposition, N could represent the global scope) shall be declared before its use in the block in which it is used or in one of its enclosing blocks (6.3) or, shall be declared before its use in namespace N or, if N is a nested namespace, shall be declared before its use in one of N’s enclosing namespaces.
Argument dependent lookup only enters the picture when a function is called, not when it is defined. These things have nothing to do with each other at all.
Name Lookup and class scope
When member functions are defined in namespace scope C++ provides special name lookup rules for unqualified names that follow the function’s declarator-id (3.4.1/8). Such names are looked up in class scope before they are looked up in namespace scope.
Since the return type in an "ordinary" member function definition precedes function’s declarator-id, the aforementioned special rules do not apply to it. It is looked up in accordance with the "usual" rules: in namespace scope.
For this reason your function definition's return type refers to ::Type
, not to Exercise::Type
. It does not match any of the declarations made inside the class. The code is ill-formed.
If you want the unqualified return type name to be looked up in class scope as well, use the new trailing return type syntax in function declaration, since in this syntax the return type follows function’s declarator-id
auto Exercise::setVal(Type parm) -> Type {
val = parm + initVal();
return val;
}
What are the pitfalls of ADL?
There is a huge problem with argument-dependent lookup. Consider, for example, the following utility:
#include <iostream>
namespace utility
{
template <typename T>
void print(T x)
{
std::cout << x << std::endl;
}
template <typename T>
void print_n(T x, unsigned n)
{
for (unsigned i = 0; i < n; ++i)
print(x);
}
}
It's simple enough, right? We can call print_n()
and pass it any object and it will call print
to print the object n
times.
Actually, it turns out that if we only look at this code, we have absolutely no idea what function will be called by print_n
. It might be the print
function template given here, but it might not be. Why? Argument-dependent lookup.
As an example, let's say you have written a class to represent a unicorn. For some reason, you've also defined a function named print
(what a coincidence!) that just causes the program to crash by writing to a dereferenced null pointer (who knows why you did this; that's not important):
namespace my_stuff
{
struct unicorn { /* unicorn stuff goes here */ };
std::ostream& operator<<(std::ostream& os, unicorn x) { return os; }
// Don't ever call this! It just crashes! I don't know why I wrote it!
void print(unicorn) { *(int*)0 = 42; }
}
Next, you write a little program that creates a unicorn and prints it four times:
int main()
{
my_stuff::unicorn x;
utility::print_n(x, 4);
}
You compile this program, run it, and... it crashes. "What?! No way," you say: "I just called print_n
, which calls the print
function to print the unicorn four times!" Yes, that's true, but it hasn't called the print
function you expected it to call. It's called my_stuff::print
.
Why is my_stuff::print
selected? During name lookup, the compiler sees that the argument to the call to print
is of type unicorn
, which is a class type that is declared in the namespace my_stuff
.
Because of argument-dependent lookup, the compiler includes this namespace in its search for candidate functions named print
. It finds my_stuff::print
, which is then selected as the best viable candidate during overload resolution: no conversion is required to call either of the candidate print
functions and nontemplate functions are preferred to function templates, so the nontemplate function my_stuff::print
is the best match.
(If you don't believe this, you can compile the code in this question as-is and see ADL in action.)
Yes, argument-dependent lookup is an important feature of C++. It is essentially required to achieve the desired behavior of some language features like overloaded operators (consider the streams library). That said, it's also very, very flawed and can lead to really ugly problems. There have been several proposals to fix argument-dependent lookup, but none of them have been accepted by the C++ standards committee.
Related Topics
What Happens When a Function That Returns an Object Ends Without a Return Statement
Date/Time Conversion: String Representation to Time_T
Deleted Function in Std::Pair When Using a Unique_Ptr Inside a Map
In C++ 11, How to Invoke an Arbitrary Callable Object
Why Can't I Replace Std::Map with Std::Unordered_Map
Gcc Can Compile a Variadic Template While Clang Cannot
Memory Allocation and Deallocation Across Dll Boundaries
C++ Double Dispatch for Equals()
Convert Integer to Binary and Store It in an Integer Array of Specified Size:C++
Unique_Ptr and Openssl's Stack_Of(X509)*
Using << Operator to Write to Both a File and Cout
The Most Efficient Way to Reverse a Number
Calculating the Info-Hash of a Torrent File
Reading JSON File with C++ and Boost
Referencing Memory Operands in .Intel_Syntax Gnu C Inline Assembly
Want to Efficiently Overcome Mismatch Between Key Types in a Map in Boost.Interprocess Shared Memory
Is It Ok to Specialize Std::Numeric_Limits<T> for User-Defined Number-Like Classes