整数类型的模板特化如何工作?

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

所以这是我不明白的地方:

  1. 如果 longlong long 是同一类型,为什么编译器允许两种特化共存?
  2. 为什么为 int64_t 添加专业化会产生错误?
  3. 为什么 foo<int64_t> 解析为 foo<long long> 而不是 foo<long>
  4. 为什么 foo<ssize_t> 解析为 foo<long> 而不是 foo<long long>
  5. 为什么foo<uint64_t>不使用专业化foo<size_t>
  6. 我在这里观察到的行为是通用的还是特定于机器的?我如何确定此代码是可移植的?

1) If long and long long is the same type, why does the compiler allow both specializations to coexist?

因为 longlong long 可以在相同的低级类型上实现,但从语言的角度来看,它们是不同的基本类型。

2) Why adding a specialization for int64_t produces an error?

因为std::int64_t不是算术基本类型而是另一种类型的别名(通过typedefusing定义)

3) Why foo<int64_t> resolves as foo<long long> and not foo<long>?

因为,在您的平台中,std::int64_t 被定义为 long long 的别名,而不是 long 的别名(或别名的别名...);因此,在您的平台中,std::int64_tlong 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_tstd::size_t 不是基本算术类型,但它们都是其他类型的别名(unsigned longunsigned 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) 点之外(这是永远正确的,因为 longlong long 之间的差异是语言的一部分),在很大程度上取决于平台。

但是可以使用 std::is_same 和其他类型特征来管理它。

c++中,两种类型尽管相同,但可能不同。例如,charunsigned charsigned char 相同,但仍然是不同的类型。在您的情况下,longlong long 相同但不同。这类似于 struct A{};struct B{}; 是相同但不同的类型。

另外需要理解的是 typedefusing 而不是 创建一个新类型。

  1. If long and long long is the same type, why does the compiler allow both specializations to coexist?

类型 longlong long 是不同的类型,即使它们具有相同的大小。

  1. Why adding a specialization for int64_t produces an error?

其他 built-in 类型的固定宽度整数类型是 typedefs。在您的例子中,int64_tlong intlong long int 的类型定义。您已经为作为别名的任何类型提供了专门化。与前一种情况不同,int64_t 没有命名不同的类型。

  1. Why foo<int64_t> resolves as foo<long long> and not foo<long>?
  2. Why foo<ssize_t> resolves as foo<long> and not foo<long long>?

它可能会解决一个或另一个。这取决于源代码编译的平​​台。

  1. Why foo<uint64_t> does not use specialization foo<size_t>?

同样,uint64_tsize_t 别名的类型取决于平台。似乎在这种情况下,它们只是为不同的类型起别名。

  1. 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,而不是对您使用的类型做任何假设。