C++ Function Template Default Arguments Syntax Rules
Default template arguments for function templates allow developers to specify fallback types or values when an argument is not explicitly provided and cannot be deduced. Introduced in C++11, these rules differ significantly from default arguments in class templates and regular function parameters. This article outlines the fundamental syntax rules, positioning constraints, redeclaration restrictions, and deduction behaviors governing default template arguments in C++ function templates.
Language Support and Basic Syntax
Prior to C++11, default template arguments were strictly limited to class templates. Since C++11, function templates fully support default template arguments for both type parameters and non-type template parameters.
The basic syntax mirrors that of class templates:
template <typename T = int, int N = 10>
void printValue(T val = T()) {
// Implementation
}In this example, if T and N are omitted
during invocation and cannot be deduced, T defaults to
int and N defaults to 10.
Non-Trailing Default Arguments Are Allowed
Unlike ordinary function parameters and class template parameters, a default template argument in a function template does not need to appear at the end of the template parameter list.
A parameter with a default argument can be followed by parameters without default arguments, provided that the trailing parameters can be deduced from the function's runtime arguments:
template <typename T = int, typename U>
void process(U value) {
// T defaults to int if not explicitly provided
// U is deduced from the argument 'value'
}
int main() {
process(3.14); // T = int, U = double
process<char>(3.14); // T = char, U = double
}Because template argument deduction can resolve trailing parameters from the call arguments, the compiler does not require default template arguments to be right-aligned.
Redeclaration Restrictions
In standard C++, default template arguments for function templates cannot be added or modified in subsequent declarations.
- All default template arguments must be specified in the first declaration of the function template within a translation unit.
- You cannot specify a default argument in a definition if a prior declaration omitted it.
- You cannot "accumulate" defaults across multiple declarations in the same scope, which contrasts with the behavior of default runtime function arguments.
// First declaration:
template <typename T = int>
void execute();
// Ill-formed redeclaration:
template <typename T = int>
void execute() {} // Error: redefinition of default template argumentParameter Dependency Rules
A default template argument can refer to any preceding template parameter, but it cannot refer to subsequent template parameters:
template <typename T, typename U = T>
void pairUp(T first, U second) {
// Valid: U defaults to the deduced or provided type of T
}Attempting to define
template <typename T = U, typename U> is ill-formed
because U has not yet been declared at the point
T specifies its default.
Parameter Packs Cannot Have Default Arguments
A template parameter pack cannot have a default template argument:
template <typename... Args = int> // Error: template parameter pack cannot have a default
void variadicFunc();However, a template parameter with a default argument can precede a parameter pack if the parameter pack can be deduced:
template <typename T = int, typename... Rest>
void variadicFunc(Rest... args); // Valid: Rest can be deduced as empty or populatedInteraction with Explicit Specialization and Overload Resolution
When calling a function template:
- Explicitly provided template arguments always take precedence over default template arguments.
- If an explicit argument is omitted, the compiler first attempts to deduce the parameter from the function call arguments.
- If deduction does not produce a type and no explicit argument was supplied, the default template argument is substituted.
- If deduction fails and no default exists, substitution fails (SFINAE) or a compile-time error occurs.