整数类型的模板特化如何工作?
How does template specialization with integer types work?
我有一个带有单个参数的模板函数 <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>
?
- 我在这里观察到的行为是通用的还是特定于机器的?我如何确定此代码是可移植的?
1) If long
and long long
is the same type, why does the compiler allow both specializations to coexist?
因为 long
和 long long
可以在相同的低级类型上实现,但从语言的角度来看,它们是不同的基本类型。
2) Why adding a specialization for int64_t
produces an error?
因为std::int64_t
不是算术基本类型而是另一种类型的别名(通过typedef
或using
定义)
3) Why foo<int64_t>
resolves as foo<long long>
and not foo<long>
?
因为,在您的平台中,std::int64_t
被定义为 long long
的别名,而不是 long
的别名(或别名的别名...);因此,在您的平台中,std::int64_t
是 long long
;在不同的平台,你可以有不同的结果
4) Why foo<ssize_t>
resolves as foo<long>
and not foo<long long>
?
与 std::int64_t
相同:类型 ssize_t
(不是标准类型)是 long
的别名(在您的平台中),而不是 long long
5) Why foo<uint64_t>
does not use specialization foo<size_t>
?
因为 std::uint64_t
和 std::size_t
不是基本算术类型,但它们都是其他类型的别名(unsigned long
和 unsigned long long
,我想)并且,在你的平台,它们是不同类型的别名
6) Is the behavior that I observe here universal, or machine-specific? How can I be sure that this code is portable?
除了第 (1) 点之外(这是永远正确的,因为 long
和 long long
之间的差异是语言的一部分),在很大程度上取决于平台。
但是可以使用 std::is_same
和其他类型特征来管理它。
在c++
中,两种类型尽管相同,但可能不同。例如,char
与 unsigned char
或 signed char
相同,但仍然是不同的类型。在您的情况下,long
和 long long
相同但不同。这类似于 struct A{};
和 struct B{};
是相同但不同的类型。
另外需要理解的是 typedef
和 using
到 而不是 创建一个新类型。
- If
long
and long long
is the same type, why does the compiler allow both specializations to coexist?
类型 long
和 long long
是不同的类型,即使它们具有相同的大小。
- Why adding a specialization for
int64_t
produces an error?
其他 built-in 类型的固定宽度整数类型是 typedef
s。在您的例子中,int64_t
是 long int
或 long long int
的类型定义。您已经为作为别名的任何类型提供了专门化。与前一种情况不同,int64_t
没有命名不同的类型。
- Why
foo<int64_t>
resolves as foo<long long>
and not foo<long>
?
- Why
foo<ssize_t>
resolves as foo<long>
and not foo<long long>
?
它可能会解决一个或另一个。这取决于源代码编译的平台。
- Why
foo<uint64_t>
does not use specialization foo<size_t>
?
同样,uint64_t
和 size_t
别名的类型取决于平台。似乎在这种情况下,它们只是为不同的类型起别名。
- Is the behavior that I observe here universal, or machine-specific? How can I be sure that this code is portable?
您观察到的大多数行为都取决于平台。尽管可移植性并不意味着行为在所有平台上都相同,只是它会在所有平台上做正确的事情。如果您的目的是显示 int
的大小,那么在具有不同大小 int
的平台上,行为会有所不同是正常的。最终,这里的可移植性错误是假设相同的类型是相同的类型。
如果您的代码取决于这些类型的具体细节,您可以使用 std::numeric_limits
and on the <type_traits>
header,而不是对您使用的类型做任何假设。
我有一个带有单个参数的模板函数 <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>
? - 我在这里观察到的行为是通用的还是特定于机器的?我如何确定此代码是可移植的?
1) If
long
andlong long
is the same type, why does the compiler allow both specializations to coexist?
因为 long
和 long long
可以在相同的低级类型上实现,但从语言的角度来看,它们是不同的基本类型。
2) Why adding a specialization for
int64_t
produces an error?
因为std::int64_t
不是算术基本类型而是另一种类型的别名(通过typedef
或using
定义)
3) Why
foo<int64_t>
resolves asfoo<long long>
and notfoo<long>
?
因为,在您的平台中,std::int64_t
被定义为 long long
的别名,而不是 long
的别名(或别名的别名...);因此,在您的平台中,std::int64_t
是 long long
;在不同的平台,你可以有不同的结果
4) Why
foo<ssize_t>
resolves asfoo<long>
and notfoo<long long>
?
与 std::int64_t
相同:类型 ssize_t
(不是标准类型)是 long
的别名(在您的平台中),而不是 long long
5) Why
foo<uint64_t>
does not use specializationfoo<size_t>
?
因为 std::uint64_t
和 std::size_t
不是基本算术类型,但它们都是其他类型的别名(unsigned long
和 unsigned long long
,我想)并且,在你的平台,它们是不同类型的别名
6) Is the behavior that I observe here universal, or machine-specific? How can I be sure that this code is portable?
除了第 (1) 点之外(这是永远正确的,因为 long
和 long long
之间的差异是语言的一部分),在很大程度上取决于平台。
但是可以使用 std::is_same
和其他类型特征来管理它。
在c++
中,两种类型尽管相同,但可能不同。例如,char
与 unsigned char
或 signed char
相同,但仍然是不同的类型。在您的情况下,long
和 long long
相同但不同。这类似于 struct A{};
和 struct B{};
是相同但不同的类型。
另外需要理解的是 typedef
和 using
到 而不是 创建一个新类型。
- If
long
andlong long
is the same type, why does the compiler allow both specializations to coexist?
类型 long
和 long long
是不同的类型,即使它们具有相同的大小。
- Why adding a specialization for
int64_t
produces an error?
其他 built-in 类型的固定宽度整数类型是 typedef
s。在您的例子中,int64_t
是 long int
或 long long int
的类型定义。您已经为作为别名的任何类型提供了专门化。与前一种情况不同,int64_t
没有命名不同的类型。
- Why
foo<int64_t>
resolves asfoo<long long>
and notfoo<long>
?- Why
foo<ssize_t>
resolves asfoo<long>
and notfoo<long long>
?
它可能会解决一个或另一个。这取决于源代码编译的平台。
- Why
foo<uint64_t>
does not use specializationfoo<size_t>
?
同样,uint64_t
和 size_t
别名的类型取决于平台。似乎在这种情况下,它们只是为不同的类型起别名。
- Is the behavior that I observe here universal, or machine-specific? How can I be sure that this code is portable?
您观察到的大多数行为都取决于平台。尽管可移植性并不意味着行为在所有平台上都相同,只是它会在所有平台上做正确的事情。如果您的目的是显示 int
的大小,那么在具有不同大小 int
的平台上,行为会有所不同是正常的。最终,这里的可移植性错误是假设相同的类型是相同的类型。
如果您的代码取决于这些类型的具体细节,您可以使用 std::numeric_limits
and on the <type_traits>
header,而不是对您使用的类型做任何假设。