c++language-lawyername-lookup

Calling function not considered during unqualified name lookup?


Why does unqualified name lookup during template instantiation not consider the name of the calling function? Specifically, why does the static_assert fail in the following examples?

Example 1: Simple Non-Member Function

template <typename T>
inline constexpr bool has_foo = requires(T& t) { foo(t); };
void foo(int x) { static_assert(has_foo<int>); }

int main() { foo(42); }

LIVE

Example 2: Non-Member Template Function

template <typename T>
inline constexpr bool has_foo = requires(T& t) { foo(t); };
void foo(auto x) { static_assert(has_foo<decltype(x)>); }

int main() { foo(42); }

LIVE

In both cases, the assert fires.

In both cases, the static_assert fails. I have reviewed the following resources but have not found a clear explanation for this behavior:

Can someone explain why the name of the calling function is not considered during unqualified name lookup in these scenarios?


Solution

  • C++ has long had the rule that name lookup in templates is done from the point of definition, except that if the name is dependent then argument-dependent lookup is also done from the point of instantiation (and this part looks only in the associated namespaces, not in the scopes surrounding the definition). This can be viewed as a compromise between two objectives.

    Since the introduction of modules in C++20 we no longer do the argument-dependent part of the lookup from the "point of instantiation" but rather the instantiation context, which is a set consisting of possibly multiple points. (Note that the instantiation context for an instantiated template specialization always includes at least the point of instantiation.)

    In the current draft (at the time of writing of this answer), [basic.lookup.unqual]/4 is the rule that specifies that unqualified lookup finds only those declarations that are declared prior to the occurrence of the name being looked up:

    An unqualified name is [...] Unless otherwise specified, such a name undergoes unqualified name lookup from the point where it appears.

    And [basic.lookup.argdep]/4 is the rule that allows the argument-dependent portion of the lookup to find names that may be declared after the point where the name is used:

    [...] If the lookup is for a dependent name ([temp.dep], [temp.dep.candidate]), the above lookup is also performed from each point in the instantiation context ([module.context]) of the lookup, additionally ignoring any declaration that appears in another translation unit, is attached to the global module, and is either discarded ([module.global.frag]) or has internal linkage.