Argument-Dependent Lookup

Argument-dependent lookup (ADL) is a feature of C++ that allows namespaced functions to be called in some contexts even without a namespace qualification. If a function call expression has a type in some namesapce ns, then the called function may be found in ns, even if it is not prefixed with ns::. The details about which namespaces are considered can get a bit gnarly, but Abseil Tip #49 is a great explanation about what's going on.

Unfortunately, ADL can get in the way of a lot of refactorings. We provide an empty class template bronto::avoid_adl<...> to automatically qualify calls that rely on ADL. For example,

bronto::avoid_adl Example.
namespace ns {

struct S : bronto::avoid_adl<S> {};
void accepts_s(S const&);

}  // namespace ns

void examples(ns::S s) {
  accepts_s(s);  // Will be qualified as `ns::accepts_s`
}

Semantics of bronto::avoid_adl

Roughly speaking, if a type inherits from bronto::avoid_adl<Ts...>, unqualified function with arguments of that type may be rewritten to use qualifiers. The call will be rewritten if this was the only way the function could be found via argument-dependent lookup. If the function call contains other arguments that trigger ADL that are not tagged with bronto::avoid_adl, the call will not be rewritten. A more formal description can be found below the examples.

Examples

bronto::avoid_adl Examples.
namespace ns {

struct A : bronto::avoid_adl<A> {};
struct B {};
struct C : bronto::avoid_adl<C> {};

void accepts_a(A);
void accepts_b(B);
void accepts_a_and_b(A, B);
void accepts_a_and_c(A, C);

}  // namespace ns

void examples(ns::A a, ns::B b, ns::C c) {
  // Will be qualified as `ns::accepts_a`
  accepts_a(a);

  // Will not be qualified because `bronto::avoid_adl` is not present.
  accepts_b(b);

  // Will not be qualified. Even though `A` is tagged with
  // `bronto::avoid_adl`, The function call can be looked up through `B`
  accepts_a_and_b(a, b);

  // Will be qualified as `ns::accepts_a_and_c`. Every argument that would
  // bring in namespace `ns` is tagged with `bronto::avoid_adl`.
  accepts_a_and_c(a, c);
}

Formal description

For each type, bronto-refactor will build a set of "desired associated namespaces" according to the rules of ADL, with the following exception: If a type directly inherits from bronto::avoid_adl<Ts...>, the associated namespaces from Ts... will not be added to the "bronto-associated namespaces".

If an unqualified function call occurs due to ADL, and the function found is in namespace ns but ns is not a "desired associated namespace" of any argument, bronto-refactor will add qualification to the function call.

See it in action on Compiler Explorer.

On this page