§10.4/3 gives all the possible situations of decl-reachable in detail. However, I can't fully understand it. Consider the example described in §10.4/6:
Source file foo.h
:
namespace N {
struct X {};
int d();
int e();
inline int f(X, int = d()) { return e(); }
int g(X);
int h(X);
}
Module M interface:
module;
#include "foo.h"
export module M;
template<typename T> int use_f() {
N::X x; // N::X, N, and :: are decl-reachable from use_f
return f(x, 123); // N::f is decl-reachable from use_f,
// N::e is indirectly decl-reachable from use_f
// because it is decl-reachable from N::f, and
// N::d is decl-reachable from use_f
// because it is decl-reachable from N::f
// even though it is not used in this call
}
template<typename T> int use_g() {
N::X x; // N::X, N, and :: are decl-reachable from use_g
return g((T(), x)); // N::g is not decl-reachable from use_g
}
template<typename T> int use_h() {
N::X x; // N::X, N, and :: are decl-reachable from use_h
return h((T(), x)); // N::h is not decl-reachable from use_h, but
// N::h is decl-reachable from use_h<int>
}
int k = use_h<int>();
// use_h<int> is decl-reachable from k, so
// N::h is decl-reachable from k
Module M implementation:
module M;
int a = use_f<int>(); // OK
int b = use_g<int>(); // error: no viable function for call to g;
// g is not decl-reachable from purview of
// module M's interface, so is discarded
int c = use_h<int>(); // OK
Why is N::g
not decl-reachable from use_g
? Why is N::h
not decl-reachable from use_h
, but N::h
is decl-reachable from use_h<int>
? Why doesn't §10.4/(3.2) or §10.4/(3.3) apply to them?
N::f
is decl-reachable from use_f
due to rule 10.4.3.2.
In determining whether N::g
is reachable from use_g
, we find that neither 10.4.3.2 nor 10.4.3.3 applies.
g((T(), x))
is a dependent call and thus, at the point of declaration of the template use_g
, it can't be determined yet which function is actually named by the call. (It will be determined when use_g
is instantiated, but in that case it may only imply that N::g
is reachable from that particular specialization of use_g
, not the template use_g
itself.)g
where each type-dependent argument is replaced by an expression of a type that has no associated namespaces or entities. Thus, for example, we could replace (T(), x)
by 0
, giving the hypothetical call g(0)
. This would not find N::g
during the name lookup phase, so it doesn't make N::g
decl-reachable.For similar reasons, N::h
is not decl-reachable from use_h
.
When use_h<int>
is instantiated, then rule 10.4.3.2 applies. At that point, the compiler determines that the type of (T(), x)
is N::X
, and actually performs the name lookup for h
, finding N::h
through argument-dependent lookup. That is, h((T(), x))
names the function N::h
in this particular specialization (where T
= int
), but not in the original template.