Assuming the use of the latest C++ standard, consider:
template<class T>
void foo(T t){ fuu<T>(); }
template<class T>
void fuu(){}
int main() {
foo(5L);
}
Is this program IFNDR or just ill-formed? I would appreciate if I can get a walkthrough of what the standard says here.
I tried reading the relevant paragraphs myself but I'm still not completely sure. It looks IFNDR according to [temp.dep.candidate]... at least from my understanding.
The reason I'm wondering is because GCC and clang output an error, while MSVC doesn't (even by using the /permissive-
flag), and I want to know whether the latter is compliant.
Here is the gcc error, for instance:
error: there are no arguments to 'fuu' that depend on a template parameter, so a declaration of 'fuu' must be available [-fpermissive]
Edit: Corrected inaccuracies after Richard Smith pointed out that I have misread the words.
Since C++20, function calls where the function is named by an unqualified template-id have been able to participate in argument-dependent lookup. However, contrary to what I said earlier, the explicitly specified template arguments for the function don't contribute to the associated entities set of associated entities.
Note that [temp.dep.general]/2 says that if such a template argument is dependent, the call is considered a dependent call:
A dependent call is an expression, possibly formed as a non-member candidate for an operator ([over.match.oper]), of the form:
- [...]
- the unqualified-id is a template-id in which any of the template arguments depends on a template parameter.
[...]
Since it's a dependent call, [basic.lookup.argdep]/4 applies:
The associated namespaces for a call are the innermost enclosing non-inline namespaces for its associated entities as well as every element of the inline namespace set ([namespace.def]) of those namespaces. Argument-dependent lookup finds all declarations of functions and function templates that
- are found by a search of any associated namespace, or
- are declared as a friend ([class.friend]) of any class with a reachable definition in the set of associated entities, or
- are exported, are attached to a named module M ([module.interface]), do not appear in the translation unit containing the point of the lookup, and have the same innermost enclosing non-inline namespace scope as a declaration of an associated entity attached to M ([basic.link]).
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.
This leads to a strange situation where fuu<T>()
is considered a dependent call (because T
is a template parameter of the enclosing function) and is therefore subject to the rule above, but the ADL can't find the fuu
declared after foo
because there are no associated namespaces for the call (and therefore, there is nothing to search). The "second phase" of the name lookup, which uses ADL, is ineffective.
Therefore, it appears that the definition of foo
is ill-formed, no diagnostic required: no valid specialization can be generated by foo
because when the second phase of name lookup is done, it will always turn out that name lookup for fuu
has failed since no declarations after foo
will be found. See [temp.res.general]/6.1. But once you have an actual instantiation of foo
, as in the OP's program, a diagnostic becomes required (note that [temp.res.general]/6.1 has "and the innermost enclosing template is not instantiated" as a condition).
Note: It's been argued in the comments to the question that fuu<T>
fails to be a valid template-id because fuu
hasn't yet been introduced as a template-name. However, if this argument were correct, it would imply that many dependent calls would be syntactically invalid because the identifier that names the function would fail to be an id-expression; see [expr.post], [expr.prim.grammar], [expr.prim.id.unqual]/1. I think the intent of the standard is to treat fuu
as a template-name in this context.