[英]Is it possible to treat a pointer as an integral type in non-type template parameters?
[英]Template partial specialization for integral non-type parameters and non-integral non-types, difference between g++ and clang
// #1
template <typename T, T n1, T n2>
struct foo {
static const char* scenario() {
return "#1 the base template";
}
};
// #2
// partial specialization where T is unknown and n1 == n2
template <typename T, T a>
struct foo<T, a, a> {
static const char* scenario() {
return "#2 partial specialization";
}
};
下面的主要内容在g++ (6.1)
和clang++ (3.8.0)
上得到不同的结果:
extern const char HELLO[] = "hello";
double d = 2.3;
int main() {
cout << foo<int, 1, 2> ::scenario() << endl;
cout << foo<int, 2, 2> ::scenario() << endl;
cout << foo<long, 3, 3> ::scenario() << endl;
cout << foo<double&, d, d> ::scenario() << endl;
cout << foo<double*, &d, &d> ::scenario() << endl;
cout << foo<double*, nullptr, nullptr> ::scenario() << endl;
cout << foo<int*, nullptr, nullptr> ::scenario() << endl;
cout << foo<nullptr_t, nullptr, nullptr> ::scenario() << endl;
cout << foo<const char*, HELLO, HELLO> ::scenario() << endl;
}
g++
和clang++
的结果# | The code | g++ (6.1) | clang++ (3.8.0) |
1 | foo<int, 1, 2> | #1 as expected | #1 as expected |
2 | foo<int, 2, 2> | #2 as expected | #2 as expected |
3 | foo<long, 3, 3> | #2 as expected | #2 as expected |
4 | foo<double&, d, d> | #1 -- why? | #2 as expected |
5 | foo<double*, &d, &d> | #2 as expected | #2 as expected |
6 | foo<double*, nullptr, nullptr> | #2 as expected | #1 -- why? |
7 | foo<int*, nullptr, nullptr> | #2 as expected | #1 -- why? |
8 | foo<nullptr_t, nullptr, nullptr> | #2 as expected | #1 -- why? |
9 | foo<const char*, HELLO, HELLO> | #2 as expected | #2 as expected |
代码: https ://godbolt.org/z/4GfYqxKn3
编辑,2021 年 12 月:
自原始帖子以来的几年中,结果发生了变化,甚至在某个时间点gcc
和clang
是相同的,但是再次检查, g++ (11.2)
和clang++ (12.0.1)
改变了他们在引用上的结果(案例4),但仍然存在差异。 目前gcc
似乎一切正常,而参考案例中的clang
是错误的。
# | The code | g++ (11.2) | clang++ (12.0.1) |
1 | foo<int, 1, 2> | #1 as expected | #1 as expected |
2 | foo<int, 2, 2> | #2 as expected | #2 as expected |
3 | foo<long, 3, 3> | #2 as expected | #2 as expected |
4 | foo<double&, d, d> | #2 as expected | #1 -- why? |
5 | foo<double*, &d, &d> | #2 as expected | #2 as expected |
6 | foo<double*, nullptr, nullptr> | #2 as expected | #2 as expected |
7 | foo<int*, nullptr, nullptr> | #2 as expected | #2 as expected |
8 | foo<nullptr_t, nullptr, nullptr> | #2 as expected | #2 as expected |
9 | foo<const char*, HELLO, HELLO> | #2 as expected | #2 as expected |
让我们从根据 C++ 标准确定什么是正确的开始,并将其与实际的编译器进行比较。 我很确定,它应该像您所期望的那样工作,即第一种情况是
#1<\/code> ,其余情况是
#2<\/code> (尽管有一些警告,见后)。
正如
evzh<\/code>在他的评论中指出的那样(2020 年 7 月 1 日 3:30),gcc 7.2 和 clang 4.0.0 是最早可以正常工作的版本。
它们可以正常工作,一直到带有
-std=c++11<\/code>的最新版本。
这是警告。 当您将
-std<\/code>从
c++14<\/code>增加到
c++17<\/code>时,Clang 的工作方式不同。
在EDIT, Dec-2021<\/strong>的问题中,您使用的是
-std=c++20<\/code> ,它在
c++17<\/code>之后。
这就是为什么你经历了
why?<\/code>
案件。
我的假设是编译器在 2016 年存在错误,它们与上述已建立的正确行为不同,并且带有
-std=c++17<\/code>或更高版本的 clang 12.0.1 在
foo<double&, d, d><\/code>的情况下仍然存在错误.
Clang 是一个非常迂腐的编译器,所以我可能错了。 有一点机会,某些东西改变了标准,这使得 clang 正确而其他编译器错误。 这值得在一个单独的问题中提出。
这是一个简化的测试来演示您的所有案例: demo<\/a> 。
我添加了四个编译器来展示
foo<double&, d, d><\/code>行为方式。
#4 格式错误,我很惊讶它可以编译。 一方面,double 不能用作非类型模板参数。 另一方面,类模板使用与函数模板相同的规则进行偏序排序。 该标准提供了为执行排序而生成的“虚构”函数的示例:
template<int I, int J, class T> class X { };
template<int I, int J> class X<I, J, int> { }; // #1
template<int I> class X<I, I, int> { }; // #2
template<int I, int J> void f(X<I, J, int>); // A
template<int I> void f(X<I, I, int>); // B
对于您的示例,它看起来像这样:
template <typename T, T n1, T n2>
struct foo {
};
template <typename T, T n1, T n2>
void bar(foo<T, n1, n2>)
{
std::cout << "a";
}
template <typename T, T a>
void bar(foo<T, a, a>)
{
std::cout << "b";
}
模板参数推导用于确定哪个函数比另一个函数更专业。 double&
应该被推导为double
,因此两个特化应该相等,也就是bar(foo<double&, d, d>{});
歧义bar(foo<double&, d, d>{});
. 瞧,GCC 和 Clang 抱怨:
海湾合作委员会错误
main.cpp:14:6: 注意: 模板参数推导/替换失败: main.cpp:26:29: 注意: 'double&' 和 'double' 类型不匹配
bar(foo<double&, d, d>{}); ^
叮当错误
注意:候选模板被忽略:替换失败 [with T = double &]:推导出的非类型模板参数与其对应的模板参数的类型不同('double' vs 'double &')
同样,如果您删除引用,他们都会正确地抱怨使用double
作为非类型模板参数。
我不打算测试其余的,但你可能会发现类似的结果。
我认为在 clang nullptr
更像是内置变量,如内置类型int
。 nullptr
实际上没有类型。 nullptr_t
的声明(没有定义)是struct nullptr_t nullptr_t;
. 问题一的答案是正确的......答案是两者都是正确的,因为有标准,但这些是由不同公司制造的不同编译器,因此GNU G++
和Clang
之间可能存在差异。 Clang
应该与G++
完全兼容,但事实并非如此。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.