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

时间:2018-02-15 19:27:41

标签: c++ c++11 templates template-specialization

我有一个带有单个参数<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. 我在这里观察到的行为是通用的还是机器特定的?我如何确定此代码是否可移植?

2 个答案:

答案 0 :(得分:4)

  

1)如果longlong long是同一类型,为什么编译器允许两个特化共存?

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

  

2)为什么为int64_t添加专业化会产生错误?

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

  

3)为什么foo<int64_t>解析为foo<long long>而不是foo<long>

因为在您的平台中,std::int64_t被定义为long long的别名,而不是long(或别名的别名......);因此,在您的平台中,std::int64_tlong 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_tstd::size_t不是基本的算术类型,但它们都是其他类型的别名(unsigned longunsigned long long,我猜)并且,在您的平台中,它们是不同类型的别名

  

6)我在这里观察到的行为是通用的还是机器特定的?我如何确定此代码是否可移植?

除了point(1)之外(由于longlong long之间的差异是语言的一部分,这是正确的),因此与平台有很大关系。

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

答案 1 :(得分:3)

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

要理解的其他事项是typedefusing创建新类型。

  
      
  1. 如果longlong long属于同一类型,为什么编译器允许两种特化共存?
  2.   

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

  
      
  1. 为什么为int64_t添加专门化会产生错误?
  2.   

对于其他内置类型,固定宽度整数类型为typedef s。在您的情况下,int64_tlong intlong long int的typedef。您已经为其所属的任何类型提供了专门的服务。与前一种情况不同,int64_t没有命名不同的类型。

  
      
  1. 为什么foo<int64_t>解析为foo<long long>而不是foo<long>
  2.   
  3. 为什么foo<ssize_t>解析为foo<long>而不是foo<long long>
  4.   

它可能会解决其中一个问题。这取决于源代码正在编译的平台。

  
      
  1. 为什么foo<uint64_t>不使用专业化foo<size_t>
  2.   

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

  
      
  1. 我在这里观察到的行为是通用的还是机器特定的?我如何确定此代码是否可移植?
  2.   

您观察到的大多数行为都与平台有关。虽然可移植性并不意味着所有平台上的行为都是相同的,但它只会在所有平台上做正确的事情。如果您打算显示int的大小,那么在具有不同大小int的平台上行为会有所不同,这是正常的。最终,这里的可移植性错误是假设相同的类型是相同的类型。

如果编码取决于这些类型的具体细节,您可以使用std::numeric_limits<type_traits>标题,而不是假设您使用的类型。