带非类型模板参数的函数模板前向声明及代码编译合规性咨询
Hey there! Great question—let’s break down why your code is fully compliant with C++ standard rules:
1. Default template parameters can be added in later declarations
C++ lets you add default values for template parameters in subsequent declarations (including the definition) of a function template, as long as:
- All declarations are in the same scope
- The default argument is valid for the parameter’s type
In your code:
- The forward declaration defines a non-type template parameter with type
typename std::enable_if_t<std::is_same<std::decay_t<T>, int>::value>*. When theenable_ifcondition passes (i.e., whenTdecays toint), this type resolves tovoid*. - The definition adds
= nullptras the default for that parameter—and sincenullptris a valid constant expression forvoid*, this is totally allowed by the standard.
2. SFINAE works as intended here
Your enable_if check is correctly structured:
- When you call
foo(int{}),Tgets deduced toint&&(thanks to perfect forwarding).std::decay_t<T>strips away the reference, leavingint, so theenable_ifcondition evaluates to true. - With the condition satisfied, the non-type parameter’s type becomes
void*, and the defaultnullptrfills in that parameter, letting the template instantiate successfully.
3. Forward declaration and definition match correctly
The forward declaration and definition of foo have identical template parameter type lists—only the definition adds the default argument for the non-type parameter. The standard allows this kind of incremental default argument addition for function templates, as long as the parameter types themselves stay consistent.
Modern compilers like Clang handle this pattern correctly, as you’ve seen. While older compiler versions might have had edge cases, this implementation is well within the standard’s rules.
内容的提问来源于stack exchange,提问作者Curious

