List of C++ Name Resolution (And Overloading) Rules

What are the name resolution rules in C++ namespaces?

The rules to keep in mind for using-directives are the following

[namespace.udir]

2 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
([basic.lookup.unqual]), the names appear as if they were declared in
the nearest enclosing namespace which contains both the
using-directive and the nominated namespace. [ Note: In this context,
“contains” means “contains directly or indirectly”. — end note ]

3 A using-directive does not add any members to the declarative
region in which it appears.

Paragraph 3 is what ensures that we don't get an error until name lookup is attempted. It doesn't add a declaration, and so long as str isn't looked up by unqualified name lookup, we get no conflict. Whereas paragraph 2 tells us how the names are logically laid out. We can apply it to your two examples.

  1. The nearest enclosing namespace that contains both the using namespace ns1; and the namespace ns1 itself is the global namespace. So the name ns1::str behaves as if we had written

    static auto str = "ns1::str";
    namespace ns2 {
    static auto str = "ns2::str";
    namespace sub {
    auto f() { return str; }
    }
    }

    So, name lookup would only find ns2::str, because it would hide any str from and enclosing namespace (which is the "as-if" situation in the code above).

  2. Again, the nearest enclosing namespace is the global one. But this time, the layout we get is different

    static auto str = "str"; // This is the one declared
    static auto str = "ns1::str"; // This is the one "as-if" made available

    namespace ns2 {
    auto f() { return str; }
    }

    Obviously, the above "equivalent" code snippet isn't a valid one, the declarations would be conflicting ones. But it is an illustration to the source of the conflict. The name coming from that scope is in conflict. So unqualified name lookup fails.

In the absence of using-directives, unqualified name lookup behaves in a fairly intuitive manner. Scopes are looked up from inner to outer, until a declaration for a name is found.

what are the overload resolution rules of list-initialization

Because for #1, the copy and move constructors are disallowed by [over.best.ics]/4 (emphasized mine):

However, if the target is

  • the first parameter of a constructor or
  • the implicit object parameter of a user-defined conversion function

and the constructor or user-defined conversion function is a candidate
by

  • [over.match.ctor], when the argument is the temporary in the second step of a class copy-initialization,

  • [over.match.copy], [over.match.conv], or [over.match.ref] (in all cases), or

  • the second phase of [over.match.list] when the initializer list has exactly one element that is itself an initializer list, and the target
    is the first parameter of a constructor of class X, and the conversion
    is to X or reference to cv X,


user-defined conversion sequences are not considered. [ Note: These
rules prevent more than one user-defined conversion from being applied
during overload resolution, thereby avoiding infinite recursion.
 — end note ]

So it is the language rule that distinguishes ({...}) and {{...}}. Note the ({...}) case falls into [over.match.ctor] but the argument is NOT the temporary in the second step of a class copy-initialization, so the first bullet does not apply.

You can further read Issue 2076 to see it is intended to disallow the copy and move constructors for the inner brace in the {{...}} case:

The resolution of issue 1467 made some plausible constructs
ill-formed. For example,

struct A { A(int); };
struct B { B(A); };
B b{{0}};

This is now ambiguous, because the text disallowing user-defined
conversions for B's copy and move constructors was removed from
16.3.3.1 [over.best.ics] paragraph 4...

Operator overloading, name resolution and namespaces

In the templated case, when compiling f2, the compiler considers using Foo::operator* but not Bar::operator*, while in the non-templated case, it considers using both (and refuses to go further because of the ambiguity). What makes the compiler behave differently ?

In both cases the compiler considers using both, but in the case of a templated operator*, the call is not ambiguous since there is a non-templated function which parameter types perfectly matches the arguments (try replace 3.f with 3. and you will see that the templated version is found). Typically:

template <typename T>
void g (T) { }

void g (float) { }

g(0.f); // Ok, the overload for float is preferred over the templated version

A user of my library Bar will be outside the Bar:: namespace, yet I want Bar::operator* to be used, and not Foo::operator*. I considered explicitely calling Bar::operator*(3.f,a), which is ugly, or inserting my own operator in the global namespace, which I reckon is a Bad Thing. Is there an option I am missing, or am I doing something wrong ?

Unfortunately, ADL will not find your overload since the only parameters of operator* are float and MyDeriv which are defined inside the namespace Foo. One possible way would be to inherit from Foo::Deriv:

namespace Bar {
struct MyDeriv: public Foo::Deriv {};
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}

Another one is to declare your overload for operator* inside the Foo namespace:

namespace Bar {
typedef Foo::Deriv MyDeriv;
}

namespace Foo {
Bar::MyDeriv operator* (float x, const Bar::MyDeriv& d) { return Bar::MyDeriv(); }
}

operator overload resolution (C++)

As namespace b is nested inside namespace a, names declared in b will hide names declared in a. When name lookup occurs in a::b::print, it searches b, and if it doesn't find what it's looking for continues searching in a. So it does find operator<< in b and stops looking, and does not even consider the proper one in a. When you comment it out, it doesn't find it in b, and continues searching into a and finds it. You could fix this by adding this in namespace b:

using a::operator<<; 

However, the real problem with your code is that you're not utilizing ADL (argument dependent lookup) properly. Operators that work on user defined types should be in the same namespace as the types they operate on. What ADL does is add extra namespaces to search, so that any arguments (or template parameters) involved with the call will have their namespace considered--automatically. (This is why you can use the operators provided in the std namespace for builtin types when your code is outside std.)

So move operator<< out of namespace a and into the same (global) namespace where Foo is. Or, probably better, move Foo into namespace a.

Overload resolution and arrays: which function should be called?

First, the conversion sequence of all three is the same, except that for the first two, there is an lvalue transformation (lvalue to rvalue conversion), which however is not used in ordering conversion sequences. All three are exact matches (the function template specialization has parameter type char const(&)[2]).

If you iterate over the rules at 13.3.3.2p3, you stop at this paragraph

S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

A conversion sequence cannot be formed if it requires binding an rvalue reference to an lvalue, the spec says at 13.3.3.1.4p3. If you look at how reference binding works at 8.5.3p5 last bullet, it will create a temporary (I think they meant rvalue temporary) of type char const* from the array lvalue and bind the reference to that temporary. Therefor, I think (1) is better than (2). Same holds for (1) against (3), although we wouldn't need this because (3) is a template so in a tie, we would choose (1) again.

In n3225, they changed the reference binding rules so that rvalue references can bind to initializer expressions that are lvalues, as long as the reference will be bound to an rvalue (possibly created by converting the initializer properly before). This could influence the handling by Visual C++, which may not be up to date here.

I'm not sure about clang. Even if it would ignore (1), then it would end up in a tie between (2) and (3), and would need to choose (2) because it's a non-template.


I think that 8.5.3p5 last bullet is confusing because it says "Otherwise a temporary of type ..". It's not clear whether the temporary is regarded as an lvalue or as an rvalue by 13.3.3.1.4p3, which means I'm not sure how the following should really behave according to the exact words of the spec

void f(int &);
void f(int &&);

int main() {
int n = 0;
f(n);
}

If we assume the temporary is treated as an rvalue by clause 13, then we bind an rvalue ref to an rvalue in the second function and an lvalue in the first. Therefor, we will choose the second function and then get a diagnostic by 8.5.3p5 last bullet because T1 and T2 are reference-related. If we assume the temporary is treated as an lvalue by clause 13, then the following would not work

void f(int &&);
int main() {
f(0);
}

Because we would bind an rvalue ref to an lvalue which by clause 13 will make the function non-viable. And if we interpret "binding an rvalue ref to an lvalue" to refer to the initializer expression instead of the final expression bound to, we won't accept the following

void f(float &&);
int main() {
int n = 0;
f(n);
}

This however is valid as of n3225. So there seems to be some confusion - I sent a DR to the committee about this.

Overload Resolution in a Namespace

This is a problem of dependent name lookup.

In the expression overloaded(value);, the name overloaded is dependent as per [temp.dep]/1.

As far as I know, in the expression n::overloaded(value), the name overloaded (in the id-expression n::overloaded) is not dependent.


Dependent name lookup is very peculiar, [temp.dep.res]/1

In resolving dependent names, names from the following sources are considered:

  • Declarations that are visible at the point of definition of the template.
  • Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.

(There's a point of instantiation for function templates at the end of the file, so all declarations from associated namespaces can be found.)

For non-dependent names, the normal lookup rules apply (lookup from the definition context).

Therefore, to find names declared after the definition of the template, they have to be dependent and be found via ADL.


A simple workaround is to introduce another parameter to the function overloaded or wrap the argument, such that one of the arguments of this function has namespace n associated:

#include <iostream>

class C {};

namespace n {
struct ADL_helper {};
inline void overloaded(int, ADL_helper = {})
{ std::cout << "n::overloaded(int,..)" << std::endl; }
}

template<typename T> void try_it(T value) {
overloaded(value, n::ADL_helper{});
}

namespace n {
inline void overloaded(C, ADL_helper = {})
{ std::cout << "n::overloaded(C,..)" << std::endl; }
}

int main()
{
try_it(1);
C c;
try_it(c);
}

alternatively:

namespace n {
template < typename T >
struct wrapper { T elem; };

inline void overloaded(wrapper<int>)
{ std::cout << "n::overloaded(wrapper<int>)" << std::endl; }
}

template<typename T> void try_it(T value) {
overloaded(n::wrapper<T>{value});
}

namespace n {
inline void overloaded(wrapper<C>)
{ std::cout << "n::overloaded(wrapper<C>)" << std::endl; }
}

C++ overload resolution

The two “overloads” aren't in the same scope. By default, the compiler only considers the smallest possible name scope until it finds a name match. Argument matching is done afterwards. In your case this means that the compiler sees B::DoSomething. It then tries to match the argument list, which fails.

One solution would be to pull down the overload from A into B's scope:

class B : public A {
public:
using A::DoSomething;
// …
}

Overload resolution in nested namespace with parent namespace

The point is name lookup, which happens before overload resolution.

When the name foo is found at the namespace NS, name lookup stops, the further scopes won't be checked, the global foo won't be found at all. Then there's only one candidate in overload resolution, and int could convert to float implicitly, then NS::foo(float) gets called at last.

(emphasis mine)

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.



Related Topics



Leave a reply



Submit