部分模板特化的编译器错误 (c++)

Compiler errors on partial template speciailzation (c++)

我正在尝试进行简单的部分模板特化,但在 g++4.4.7、g++4.8.5、clang++3.8.0 上出现错误。每当我提到编译器错误时,我指的是所有这些的输出,因为它们总是一致的。 我正在使用 C++03,编译时没有任何选项。

代码:

#include <iostream>

template <typename T, typename X, typename G>
struct A {};

template <typename T, typename X>
struct A<T, X, void> { A() : n(1) {} X n; T b; };

template <typename X>
struct A<X, void, void> { A() : n(2) {} X n; };

int main() {
  A<int, float> one;
  A<int> two;
  std::cout << one.n << " | " << two.n << "\n";
  return 0;
}

问题一:这段代码编译失败。编译器说 A<int, float>A<int> 是错误的,因为 A 需要 3 个模板参数。为什么?

如果我把原来的声明改成

template <typename T, typename X = void, typename G = void>
struct A {};

代码编译,输出为:1 | 2.

编译器在第一步中将 onetwo 类型匹配到非特化的 A,但随后它正确地决定使用部分的代码specialized class 人们会期望它使用。但它不应该需要默认值。

然后我决定更改最后一个偏特化,切换第一个和第二个参数:

template <typename X>
struct A<void, X, void> { A() : n(2) {} X n; };

我希望这不会有任何改变,但编译器不同意。此处报告了 3 之间最清晰的输出:

a.cpp:7:40: error: field has incomplete type 'void'
struct A<T, X, void> { A() : n(1) {} X n; T b; };
                                   ^
a.cpp:14:10: note: in instantiation of template class 'A<int, void, void>'    requested here
  A<int> two;
         ^
1 error generated.

问题 2:为什么编译器将 two 变量视为仅特化一个参数的 A 的偏特化实例?

请注意这是“第二次匹配”,因为如果我只使用 1 个默认模板参数,编译器将返回抱怨需要 3 个模板参数这一事实。

谢谢。

Question 1: This code fails to compile. The compilers say that A<int, float> and A<int> are wrong as A requires 3 templates parameters. Why?

因为A需要3个模板参数。您将 A 声明为:

template <typename T, typename X, typename G>
struct A {};

A 没有两个或一个模板参数版本。有些版本专门针对某些类型 void,但这仍然是一个参数 - 而不是缺少参数。

添加默认值后,A<int, float> 的计算结果为 A<int, float, void>,这是一个有效的实例化 - 并选择将 n 设置为 1 的特化。

您误解了专业化的运作方式。专业化不会改变模板参数的数量。这只是一种根据模板参数最终成为什么来添加特殊功能的方法。

Question 2: Why are the compilers considering the two variable an instance of the partial specialization of A that specializes only one argument?

我们有三种选择

template <T, X, G>       struct A; // the primary
template <T, X, void>    struct A; // (1)
template <void, X, void> struct A; // (2)

当我们实例化A<int>时,与我们在默认参数中加入A<int, void, void>是一样的。那不匹配 (2) - 因为那个要求第一个参数是 void 而你的是 int(1) 比主匹配更好,因为它更专业。但是,(1) 有一个 X 类型的成员,在这种情况下 X 被推断为 void (从默认参数),这是不允许的。