I am playing a bit with static polymorphism, I\'m calling a function which internally calls the \"right\" specialized function depending on the type of the initial argument (bas
f_helper(typename T::tag_type{})
is a type-dependent expression because T::tag_type
is a dependent type. This means that f_helper
doesn't need to be visible until f<T>
is instantiated due to two phase lookup.
EDIT: I'm pretty sure that this is actually undefined behaviour. If we look at 14.6.4.2 [temp.dep.candidate] we see this passage:
For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that:
— For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.
— For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.
If the function name is an unqualified-id and 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.
The last paragraph to me indicates this is undefined behaviour. The function call that depends on a template parameter
here is f_helper(typename T::tag_type{})
. f_helper
isn't visible when f
is instantiated, but it would be if we performed name lookup after all translation units have been compiled.
The call to f_helper(typename T::tag_type{});
is dependent on the template parameter T
, so the name f_helper
need not be visible until the point of instantiation of f<T>
(due to two phase name lookup).
I believe the code works because implementations are allowed to delay the point of instantiation of function templates until the end of the translation unit, at which time the definitions for f_helper
are available.
N3936 §14.6.4.1/8 [temp.point]
A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.
I agree, the code is ill-formed. I'm surprised neither g++ nor clang++ has even a warning about this.
14.6.2/1:
In an expression of the form:
- postfix-expression
(
expression-list [opt])
where the postfix-expression is an id-expression, the id-expression denotes a dependent name if any of the expressions in the expression-list is a type-dependent expression (14.6.2.2) or if the unqualified-id of the id-expression is a template-id in which any of the template arguments depends on a template parameter. ... Such names are unbound and are looked up at the point of the template instantiation (14.6.4.1) in both the context of the template definition and the context of the point of instantiation.
[f_helper
is a postfix-expression and id-expression, and typename T::tag_type{}
is type-dependent, so f_helper
is a dependent name.]
14.6.4/1:
In resolving dependent names, names from the following sources are considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.
14.6.4.1/6:
The instantiation context of an expression that depends on the template arguments is the set of declarations with external linkage declared prior to the point of instantiation of the template specialization in the same translation unit.
14.6.4.2/1:
For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that:
For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.