模板默认类型与默认值
Template default type vs default value
这个问题在之后。
案例 1:默认类型
以下程序无法编译并报告error C2995: 'T foo(void)': function template has already been defined
:
#include <iostream>
#include <type_traits>
template < typename T, typename = std::enable_if_t< std::is_integral<T>::value> >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, typename = std::enable_if_t< !std::is_integral<T>::value> >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
int main() {
foo<int>();
foo<float>();
}
每个模板都被两个 foo
实例 交替 使用和忽略 (SFINAE)。所以我假设编译器在某个时候看到:
template < typename T, typename = void >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, typename = void >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
两个定义相同,错误有点可以理解。也许不太容易理解的是为什么编译器此时没有分配不同的内部函数名称。
情况 2:默认值
现在,程序可以修复,而不是使用默认类型,我们使用默认值:
template < typename T, std::enable_if_t< std::is_integral<T>::value>* = nullptr >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, std::enable_if_t< !std::is_integral<T>::value>* = nullptr >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
在这里,按照同样的程序,我得出:
template < typename T, void* = nullptr >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, void* = nullptr >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
如果那是替换,将会有相同的定义并且不会编译。很明显编译器没有这样做,或者如果是,它不会就此停止并以类似以下的方式结束:
int foo_int() { std::cout << "integral" << std::endl; return int(); }
float foo_float() { std::cout << "non-integral" << std::endl; return float(); }
int main() {
foo_int();
foo_float();
}
为什么编译器在第二种情况下设法获得了两个不同的函数,而在第一种情况下却没有?
该标准指定了哪种算法来解释模板默认类型与默认值?
Here, following the same procedure, I derive:
在那之前一切都是正确的。您有两个函数模板(忽略默认值):
template < typename T, std::enable_if_t< std::is_integral<T>::value>*>
T foo();
template < typename T, std::enable_if_t< !std::is_integral<T>::value>*>
T foo();
两个非类型模板参数没有类型void*
。它们的类型分别为 std::enable_if_t<std::is_integral<T>::value>*
和 std::enable_if_t<!std::is_integral<T>::value>*
。那些不是同一类型的。甚至不存在一个 T
,在替换之后,它们是相同的类型。
具体规则在[temp.over.link]:
Two expressions involving template parameters are considered equivalent if two function definitions containing
the expressions would satisfy the one-definition rule (3.2), except that the tokens used to name the template
parameters may differ as long as a token used to name a template parameter in one expression is replaced by
another token that names the same template parameter in the other expression. For determining whether two
dependent names (14.6.2) are equivalent, only the name itself is considered, not the result of name lookup in
the context of the template.
Two function templates are equivalent if they are declared in the same scope, have the same name, have
identical template parameter lists, and have return types and parameter lists that are equivalent using the
rules described above to compare expressions involving template parameters. Two function templates are
functionally equivalent if they are equivalent except that one or more expressions that involve template
parameters in the return types and parameter lists are functionally equivalent using the rules described
above to compare expressions involving template parameters. If a program contains declarations of function
templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is
required.
这两个函数没有相同的模板参数列表。
这个问题在
案例 1:默认类型
以下程序无法编译并报告error C2995: 'T foo(void)': function template has already been defined
:
#include <iostream>
#include <type_traits>
template < typename T, typename = std::enable_if_t< std::is_integral<T>::value> >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, typename = std::enable_if_t< !std::is_integral<T>::value> >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
int main() {
foo<int>();
foo<float>();
}
每个模板都被两个 foo
实例 交替 使用和忽略 (SFINAE)。所以我假设编译器在某个时候看到:
template < typename T, typename = void >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, typename = void >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
两个定义相同,错误有点可以理解。也许不太容易理解的是为什么编译器此时没有分配不同的内部函数名称。
情况 2:默认值
现在,程序可以修复,而不是使用默认类型,我们使用默认值:
template < typename T, std::enable_if_t< std::is_integral<T>::value>* = nullptr >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, std::enable_if_t< !std::is_integral<T>::value>* = nullptr >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
在这里,按照同样的程序,我得出:
template < typename T, void* = nullptr >
T foo() { std::cout << "integral" << std::endl; return T(); }
template < typename T, void* = nullptr >
T foo() { std::cout << "non-integral" << std::endl; return T(); }
如果那是替换,将会有相同的定义并且不会编译。很明显编译器没有这样做,或者如果是,它不会就此停止并以类似以下的方式结束:
int foo_int() { std::cout << "integral" << std::endl; return int(); }
float foo_float() { std::cout << "non-integral" << std::endl; return float(); }
int main() {
foo_int();
foo_float();
}
为什么编译器在第二种情况下设法获得了两个不同的函数,而在第一种情况下却没有?
该标准指定了哪种算法来解释模板默认类型与默认值?
Here, following the same procedure, I derive:
在那之前一切都是正确的。您有两个函数模板(忽略默认值):
template < typename T, std::enable_if_t< std::is_integral<T>::value>*>
T foo();
template < typename T, std::enable_if_t< !std::is_integral<T>::value>*>
T foo();
两个非类型模板参数没有类型void*
。它们的类型分别为 std::enable_if_t<std::is_integral<T>::value>*
和 std::enable_if_t<!std::is_integral<T>::value>*
。那些不是同一类型的。甚至不存在一个 T
,在替换之后,它们是相同的类型。
具体规则在[temp.over.link]:
Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one-definition rule (3.2), except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression. For determining whether two dependent names (14.6.2) are equivalent, only the name itself is considered, not the result of name lookup in the context of the template.
Two function templates are equivalent if they are declared in the same scope, have the same name, have identical template parameter lists, and have return types and parameter lists that are equivalent using the rules described above to compare expressions involving template parameters. Two function templates are functionally equivalent if they are equivalent except that one or more expressions that involve template parameters in the return types and parameter lists are functionally equivalent using the rules described above to compare expressions involving template parameters. If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required.
这两个函数没有相同的模板参数列表。