parts/6.40.TemplatesAndGenerics-SYM.md

6.40 Templates and Generics [SYM]

6.40.1 Applicability to language

The vulnerability as described in ISO/IEC 24772-1:2024, 6.40 exists in C++ by the language mechanism of templates. While templates are a pure compile-time mechanism, their use can result in programmer confusion because of the potentially deeply-nested nature or template instantiation. Actual errors during instantiation will fail to compile. Confusion can occur when programmer mistakes lead to successful compilation but their intent is not met due to misunderstanding of the language rules.

Similar to 6.21 Namespace Issues [BJL], adding a template specialization later can cause uses of the base template to pick up this new specialization. As long as no ambiguity occurs and the new specialization is a better match it will change the behaviour of existing code that previously instantiated the base template or a different specialization. If different translation units happen to use a template with different set of specialization declarations the mechanism might lead to different instantiations of the same template with the same arguments. This results in an ODR violation and is undefined behaviour (see 6.56 [EWF]). A common mitigation is to put all specializations of a template in the same file or module as the base template declaration, or alternatively a specialization of a template together with the definition of the user-defined entity for which it is specialised.

Due to the two phase compilation model of templates, name lookup can be surprising in class templates with dependent base classes. An unqualified name used in the derived class that is defined in the base will be found in an outer namespace instead.

double foo{0};
template <typename T> 
struct base {
    int foo;
};
template <typename T> 
struct d : base<T>{
auto bar() {
return foo; // matches global foo not base<T>::foo [1]
}
};

In the above example line [1], in place of foo, name qualification either as this->foo or d::foo would refer to the member of the base class and avoid the name lookup confusion.

Function template specialization is syntactically possible, but doesn’t affect overload resolution. Overloading is the mechanism to select special-cases for functions based on their parameter types.

Generic parameters allow a mechanism to prevent accidental implicit type conversions applied by the compiler (see 6.06 [FLC]). See the following example, where the function taking an int could be called with almost any built-in type, because of implicit conversions to int. Adding a generic overload will be a better match instead.

void doit(auto x)=delete; // #1
void doit(int i) { /* only int */ }

doit(42); // compiles
doit(true); // compile error with generic overload #1

Alternatively, implicit conversions can be disabled through a generic parameter with the concept std::same_as.

void doit(std::same_as<int> auto i){}

Employing static_assert with a condition checking the appropriateness of a template parameter in a template body will cause a compile error if the condition is false. Alternatively, one can use concepts to limit template instantiations so that alternative specializations or overloads can to be considered.

In class templates a generic constructor or assignment operator never matches the compiler-provided definitions of copy or move operations.

6.40.2 Avoidance mechanisms for language users

To avoid the vulnerability or mitigate its ill effects, C++ software developers can: