部分模板规范的编译器错误(c ++)

Dir*_*ich 2 c++ templates partial-specialization

我试图做一个简单的部分模板专业化,但我在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;
}
Run Code Online (Sandbox Code Playgroud)

问题1:此代码无法编译.编译器说,A<int, float>A<int>是错误的,A需要3个模板参数.为什么?

如果我将原始声明更改为

template <typename T, typename X = void, typename G = void>
struct A {};
Run Code Online (Sandbox Code Playgroud)

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

会发生的是,第一步中的编译器匹配onetwo键入非专用的A,但它正确地决定使用人们期望它使用的部分专用类的代码.但它不应该需要默认值.

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

template <typename X>
struct A<void, X, void> { A() : n(2) {} X n; };
Run Code Online (Sandbox Code Playgroud)

我希望这不会改变,但编译器不同意.这里报告了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.
Run Code Online (Sandbox Code Playgroud)

问题2:为什么编译器将two变量视为A的部分特化的一个实例,它只专用一个参数?

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

谢谢.

Bar*_*rry 6

问题1:此代码无法编译.编译器说,A<int, float>A<int>是错误的,A需要3个模板参数.为什么?

因为A需要3个模板参数.你宣称A:

template <typename T, typename X, typename G>
struct A {};
Run Code Online (Sandbox Code Playgroud)

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

当您添加默认值时,则A<int, float>计算为A<int, float, void>,这是一个有效的实例化 - 并选择设置n为1的特化.

你误解了专业化是如何运作的.专业化不会更改模板参数的数量.这只是添加特殊功能的一种方式,具体取决于模板参数的最终结果.

问题2:为什么编译器在考虑这两个变量的部分特化的实例时A只专用一个参数?

我们有三个选择

template <T, X, G>       struct A; // the primary
template <T, X, void>    struct A; // (1)
template <void, X, void> struct A; // (2)
Run Code Online (Sandbox Code Playgroud)

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