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.
Related
#include <iostream>
template<typename T>
void f(T x)
{
g(x); // g is a dependent name
};
void g(int a)
{
std::cout << a;
}
int main()
{
int a = 12;
f(a);
}
//this should be point of declaration for f<int>
Above code gives compilation error "‘g’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation".
Since g is a dependent name, its name should be visible at the time of instantiation. Please tell what am I missing?
The lookup in the instantiation context only considers candidates found by argument-dependent lookup. Since int has no associated namespaces or classes, that lookup finds nothing.
The GNU C++ compilers as of version 4.7 and beyond, no longer perform some extra unqualified lookups it had performed in the past, namely dependent base class scope lookups and unqualified template function lookups. (Read more)
This can be temporarily worked around by using -fpermissive.
void f() {}
namespace test
{
void f(int) {}
void g() { f(); } // error in gcc 6.2.0
}
int main()
{
test::g();
}
Compile it with g++ -std=c++1z main.cpp, the output is as follows:
main.cpp: In function 'void test::g()':
main.cpp:9:4: error: too few arguments to function 'void test::f(int)'
f(); // error in gcc
^
main.cpp:5:6: note: declared here
void f(int) {}
My compiler is gcc 6.2.0.
Why does gcc hide overloaded functions in the global namespace? Is this conforming to the C++ standard?
Why does gcc hide overloaded functions in the global namespace? Is this conforming to the C++ standards?
Yes. In short, you can't overload functions through different scopes. According to the rule of unqualified name lookup, for the invoking of f() in g(), the name f could be found inside the namespace test, then the name lookup stops; overload resolution takes place after that (based on the names found). That means f() in global namespace won't be considered at all even it looks more appropriate here.
(emphasis mine)
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.
In order to compile a function call, the compiler must first perform
name lookup, which, for functions, may involve argument-dependent
lookup, and for function templates may be followed by template
argument deduction. If these steps produce more than one candidate
function, then overload resolution is performed to select the function
that will actually be called.
You can use using to introduce the names into the same scope, i.e. to make them actual overloaded functions.
namespace test
{
using ::f; // introduce the name from global namespace
void f(int) {}
void g() { f(); } // fine
}
Consider the code below:
#include <utility>
void f(int, int);
void g(int, int);
struct functor
{
template<typename... T>
void operator()(T&&... params)
{
return f(std::forward<T>(params)...);
}
};
int main()
{
functor()(1); // can use the default value here, why?!
// g(1); // error here as expected, too few arguments
}
void f(int a, int b = 42) {}
void g(int a, int b = 24) {}
This is a thin wrapper around a function call. However, inside functor::operator(), f doesn't have its default value for the second parameter known (it is visible only after main, in the definition), so the code should not compile. g++5.2 compiles it successfully though, but clang++ spits out the expected message that one expects for compilers that perform the two-phase name lookup correctly:
error: call to function 'f' that is neither visible in the
template definition nor found by argument-dependent lookup
return f(std::forward(params)...);
Is this a gcc bug or I am missing something here? I.e., is the point of instantiation after the definition of f below main()? But even in this case, it shouldn't work, as at the second phase the function can only be found via ADL, which is not the case here.
[temp.dep.candidate]:
For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:
For the part of the lookup using unqualified name lookup ([basic.lookup.unqual]), only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.
If the call would be ill-formed or would find a better match had the lookup within the associated namespaces
considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation
contexts, then the program has undefined behavior.
Note that ADL is not even working here, as the involved types are fundamental (their set of associated namespaces is empty).
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.
Here is simple code presented which should have worked according to c++ standard I believe :
template<typename T>
void foo(T x)
{
bar(x);
void bar(int);
}
void bar(int) { }
int main()
{
foo(0);
}
Error comes as from GCC 4.7 as:
‘bar’ was not declared in this scope, and no declarations were found
by argument-dependent lookup at the point of instantiation
But in the C++ standard it's written. § 14.6.4.2 :
For a function call that depends on a template parameter, the
candidate functions are found using the usual lookup rules (3.4.1,
3.4.2, 3.4.3) except that:
— For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.
I may be have got the wrong impression of what's written, can anyone please correct me here?
You should just move the declaration of 'bar' to the top. Because at the point where the template is defined (not instantiated), before 'bar' is invoked, it hasn't be declared.