模板默认类型vs默认值

Template default type vs default value

本文关键字:vs 默认值 类型 默认      更新时间:2023-10-16

这个问题是继上一个问题之后的。


Case 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(); }

两个定义是相同的,这个错误是可以理解的。也许不太容易理解的是,为什么编译器到现在还没有分配不同的内部函数名。


Case 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();
}

为什么编译器在第二种情况下得到两个不同的函数,而不是第一种情况?

标准为解释模板默认类型和默认值指定了什么算法?

这里,按照同样的过程,我推导出:

在那之前一切都很好。您有两个函数模板(忽略默认值):

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]:

两个包含模板形参的表达式如果两个函数定义包含除了用于命名模板的令牌之外,表达式将满足单定义规则(3.2)参数可以不同,只要在一个表达式中用于命名模板参数的标记被替换为另一个标记,在另一个表达式中命名相同的模板形参。来决定两个依赖名称(14.6.2)是等价的,只考虑名称本身,而不考虑名称查找的结果模板的上下文。

如果两个函数模板在同一作用域中声明,具有相同的名称,则它们是等效的方法的返回类型和参数列表是相等的上面描述的比较包含模板形参的表达式的规则。有两个函数模板除了一个或多个涉及模板的表达式外,其它表达式在功能上是相等的使用所描述的规则,返回类型和参数列表中的参数在功能上是等价的来比较涉及模板形参的表达式。如果程序包含函数的声明模板在功能上相等但不相等,程序是病态的;没有诊断必需的。

这两个函数没有相同的模板形参表。