此问题遵循previous one。
以下程序无法编译并报告select CONCAT(tu.username,' have: ',tm.money,'$') as RESULT
from [$table_users] as tu
inner join [$table_money] as tm
on tu.ID_USER = tm.ID_USER
order by RESULT ASC;
:
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>();
}
实例化中,每个模板都是交替使用和忽略(SFINAE)。所以我假设编译器在某个时刻看到:
foo
两个定义都是相同的,错误在某种程度上是可以理解的。也许不太可以理解为什么编译器此时没有分配不同的内部函数名。
现在,程序可以修复,而不是使用默认类型,我们使用默认值:
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(); }
在这里,按照相同的程序,我推导出:
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(); }
为什么编译器设法在第二种情况下获得两种不同的功能,而不是第一种?
标准为解释模板默认类型与默认值指定了哪种算法?
答案 0 :(得分:4)
在这里,按照相同的程序,我推导出:
直到那一刻,一切都是正确的。你有两个功能模板(忽略默认值):
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)是等价的,只考虑名称本身,而不是名称查找的结果 模板的上下文。
如果两个函数模板在同一范围内声明,具有相同的名称,则它们是等效的 相同的模板参数列表,并具有使用相同的返回类型和参数列表 上述规则用于比较涉及模板参数的表达式。两个功能模板是 功能上等价,如果它们是等价的,除了涉及模板的一个或多个表达式 返回类型和参数列表中的参数在功能上等效,使用所描述的规则 上面比较涉及模板参数的表达式。如果程序包含函数声明 功能相同但不相同的模板,程序格式不正确;没有诊断 必需的。
这两个函数没有相同的模板参数列表。