模板默认类型vs默认值
Template default type vs default value
这个问题是继上一个问题之后的。
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)是等价的,只考虑名称本身,而不考虑名称查找的结果模板的上下文。
如果两个函数模板在同一作用域中声明,具有相同的名称,则它们是等效的方法的返回类型和参数列表是相等的上面描述的比较包含模板形参的表达式的规则。有两个函数模板除了一个或多个涉及模板的表达式外,其它表达式在功能上是相等的使用所描述的规则,返回类型和参数列表中的参数在功能上是等价的来比较涉及模板形参的表达式。如果程序包含函数的声明模板在功能上相等但不相等,程序是病态的;没有诊断必需的。
这两个函数没有相同的模板形参表。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 具有默认值的引用获取函数
- 当给定默认值时,为什么此模板参数推导失败
- 从具有默认值的部分指定模板类继承时发生SWIG错误,具有不带默认值的正向声明
- 格式化浮点值:返回默认值
- 如何将数组部分初始化为某个默认值?
- asn1c 不会从 asn.1 模块中提取八位字节字符串的默认值
- 创建一个包含 c++ 默认值的环境文件
- C++(和 ROS) - 包含与前向声明引用,设置默认值和类型定义
- Makefile g++ 使用命令行中的 -D 变量进行编译,默认值
- Switch 语句(字符串)一直选择默认值,除非其为零
- 如何使用默认值将枚举声明为 extern
- 如何在提升程序选项中设置矢量<矢量>的默认值<string>
- 如何使用默认值为构造函数中的枚举赋值?
- 变量始终在函数中重置为默认值
- 如何在C++中提供模板化函数作为另一个函数的参数,默认值?
- 函数不返回默认值
- 具有公共范围与专用范围的默认值的C++的不同行为
- Clang vs GCC - 可变参数模板参数包后跟默认值参数在 GCC 4.8 中有效,但在 Clang 3.5 中无
- 模板默认类型vs默认值