13 Templates [temp]

13.8 Name resolution [temp.res]

13.8.4 Dependent name resolution [temp.dep.res]

13.8.4.2 Candidate functions [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 from the template definition context ([basic.lookup.unqual], [basic.lookup.argdep]).
[Note
:
For the part of the lookup using associated namespaces ([basic.lookup.argdep]), function declarations found in the template instantiation context are found by this lookup, as described in [basic.lookup.argdep].
— end note
]
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.
[Example
:

Source file "X.h":

namespace Q {
  struct X { };
}

Source file "G.h":

namespace Q {
  void g_impl(X, X);
}

Module interface unit of M1:

module;
#include "X.h"
#include "G.h"
export module M1;
export template<typename T>
void g(T t) {
  g_impl(t, Q::X{ });   // ADL in definition context finds Q​::​g_­impl, g_­impl not discarded
}

Module interface unit of M2:

module;
#include "X.h"
export module M2;
import M1;
void h(Q::X x) {
   g(x);                // OK
}
— end example
]
[Example
:

Module interface unit of Std:

export module Std;
export template<typename Iter>
void indirect_swap(Iter lhs, Iter rhs)
{
  swap(*lhs, *rhs);     // swap not found by unqualified lookup, can be found only via ADL
}

Module interface unit of M:

export module M;
import Std;

struct S { /* ...*/ };
void swap(S&, S&);      // #1

void f(S* p, S* q)
{
  indirect_swap(p, q);  // finds #1 via ADL in instantiation context
}
— end example
]
[Example
:

Source file "X.h":

struct X { /* ... */ };
X operator+(X, X);

Module interface unit of F:

export module F;
export template<typename T>
void f(T t) {
  t + t;
}

Module interface unit of M:

module;
#include "X.h"
export module M;
import F;
void g(X x) {
  f(x);             // OK: instantiates f from F,
                    // operator+ is visible in instantiation context
}
— end example
]
[Example
:

Module interface unit of A:

export module A;
export template<typename T>
void f(T t) {
  cat(t, t);            // #1
  dog(t, t);            // #2
}

Module interface unit of B:

export module B;
import A;
export template<typename T, typename U>
void g(T t, U u) {
  f(t);
}

Source file "foo.h", not an importable header:

struct foo {
  friend int cat(foo, foo);
};
int dog(foo, foo);

Module interface unit of C1:

module;
#include "foo.h"        // dog not referenced, discarded
export module C1;
import B;
export template<typename T>
void h(T t) {
  g(foo{ }, t);
}

Translation unit:

import C1;
void i() {
   h(0);                // error: dog not found at #2
}

Importable header "bar.h":

struct bar {
  friend int cat(bar, bar);
};
int dog(bar, bar);

Module interface unit of C2:

module;
#include "bar.h"        // imports header unit "bar.h"
export module C2;
import B;
export template<typename T>
void j(T t) {
  g(bar{ }, t);
}

Translation unit:

import C2;
void k() {
   j(0);                // OK, dog found in instantiation context:
                        // visible at end of module interface unit of C2
}
— end example
]