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,
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
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.