我有一个带有单个参数<T>
的模板函数,我想对不同的整数类型进行此函数的特化。起初这似乎很明显,但经过几次尝试后我发现我并不真正理解专业化在这里是如何工作的,以及如何实现某种程度的可移植性......
这是测试程序:
// clang test.cc -std=c++11 -lc++
#include <iostream>
#include <typeinfo>
template <typename T> void foo() { std::cout << " foo<T> with T=" << typeid(T).name() << '\n'; }
template <> void foo<int>() { std::cout << " foo<int>\n"; }
template <> void foo<long>() { std::cout << " foo<long>\n"; }
template <> void foo<long long>() { std::cout << " foo<long long>\n"; }
template <> void foo<size_t>() { std::cout << " foo<size_t>\n"; }
// template <> void foo<int64_t>() { std::cout << " foo<int64_t>\n"; } // error
int main () {
std::cout << "sizeof(int)=" << sizeof(int) << ", ";
std::cout << "sizeof(long)=" << sizeof(long) << ", ";
std::cout << "sizeof(long long)=" << sizeof(long long) << ", ";
std::cout << "sizeof(size_t)=" << sizeof(size_t) << "\n";
foo<int>();
foo<long>();
foo<long long>();
foo<size_t>();
foo<ssize_t>();
foo<int8_t>();
foo<int16_t>();
foo<int32_t>();
foo<int64_t>();
foo<uint32_t>();
foo<uint64_t>();
return 0;
}
并在我的机器上生成
sizeof(int)=4, sizeof(long)=8, sizeof(long long)=8, sizeof(size_t)=8
foo<int>
foo<long>
foo<long long>
foo<size_t>
foo<long>
foo<T> with T=a
foo<T> with T=s
foo<int>
foo<long long>
foo<T> with T=j
foo<T> with T=y
所以这就是我不明白的地方:
long
和long long
属于同一类型,为什么编译器允许两种特化共存?int64_t
添加专门化会产生错误?foo<int64_t>
解析为foo<long long>
而不是foo<long>
?foo<ssize_t>
解析为foo<long>
而不是foo<long long>
?foo<uint64_t>
不使用专精foo<size_t>
? 答案 0 :(得分:4)
1)如果
long
和long long
是同一类型,为什么编译器允许两个特化共存?
因为long
和long long
可以在同一个低级别类型上实现,但从语言的角度来看,它们是不同的基本类型。
2)为什么为
int64_t
添加专业化会产生错误?
因为std::int64_t
不是算术基本类型,而是另一种类型的别名(通过typedef
或using
定义)
3)为什么
foo<int64_t>
解析为foo<long long>
而不是foo<long>
?
因为在您的平台中,std::int64_t
被定义为long long
的别名,而不是long
(或别名的别名......);因此,在您的平台中,std::int64_t
为long long
;在不同的平台上,你可以有不同的结果
4)为什么
foo<ssize_t>
解析为foo<long>
而不是foo<long long>
?
与std::int64_t
相同:类型ssize_t
(非标准类型)是long
的别名(在您的平台中),而不是long long
5)为什么
foo<uint64_t>
不使用专业化foo<size_t>
?
因为std::uint64_t
和std::size_t
不是基本的算术类型,但它们都是其他类型的别名(unsigned long
和unsigned long long
,我猜)并且,在您的平台中,它们是不同类型的别名
6)我在这里观察到的行为是通用的还是机器特定的?我如何确定此代码是否可移植?
除了point(1)之外(由于long
和long long
之间的差异是语言的一部分,这是正确的),因此与平台有很大关系。
但是可以使用std::is_same
和其他类型特征来管理它。
答案 1 :(得分:3)
在c++
中,尽管两种类型相同,但两种类型可能是截然不同的。例如,char
与unsigned char
或signed char
相同,但仍然是一种不同的类型。在您的情况下,long
和long long
相同但不同。这类似于struct A{};
和struct B{};
相同但不同的类型。
要理解的其他事项是typedef
和using
到不创建新类型。
- 如果
醇>long
和long long
属于同一类型,为什么编译器允许两种特化共存?
类型long
和long long
是不同的类型,即使它们具有相同的大小。
- 为什么为
醇>int64_t
添加专门化会产生错误?
对于其他内置类型,固定宽度整数类型为typedef
s。在您的情况下,int64_t
是long int
或long long int
的typedef。您已经为其所属的任何类型提供了专门的服务。与前一种情况不同,int64_t
没有命名不同的类型。
- 为什么
foo<int64_t>
解析为foo<long long>
而不是foo<long>
?- 为什么
醇>foo<ssize_t>
解析为foo<long>
而不是foo<long long>
?
它可能会解决其中一个问题。这取决于源代码正在编译的平台。
- 为什么
醇>foo<uint64_t>
不使用专业化foo<size_t>
?
同样,uint64_t
和size_t
别名的类型取决于平台。在这种情况下,它们似乎只是别名不同。
- 我在这里观察到的行为是通用的还是机器特定的?我如何确定此代码是否可移植?
醇>
您观察到的大多数行为都与平台有关。虽然可移植性并不意味着所有平台上的行为都是相同的,但它只会在所有平台上做正确的事情。如果您打算显示int
的大小,那么在具有不同大小int
的平台上行为会有所不同,这是正常的。最终,这里的可移植性错误是假设相同的类型是相同的类型。
如果编码取决于这些类型的具体细节,您可以使用std::numeric_limits
和<type_traits>
标题,而不是假设您使用的类型。