简单的案例。我不太明白为什么括号是调用显式实例化模板的默认ctor所必需的。 而且,为什么调用显式实例化模板的非默认ctor会给我“不完整类型”错误?
非常感谢!
// X.h
template <const int MODE>
class X{
public:
X() = default;
X(int& a) {++a;}
// define X here
};
// declare the explicit specialization
template <> class X<1>;
// Then define the default behaviors of X.
// X.cpp
#include "X.h"
template <>
class X<1>{
public:
X() = default;
X(int& a) {--a;}
// define X<1>
};
// Then specialize behavior.
// main.cpp
#include "X.h"
int main(){
X<2> x_2; // fine, instantiates the default version
X<1> x_1(); // Edit: A function declaration, as pointed out in the comment.
X<1> x_1_1; // error: aggregate ‘X<1> x_1_1’ has incomplete type and cannot be defined
int a = 0;
X<1> x_1_2(a); // error: variable ‘X<1> x_1_2’ has initializer but incomplete type
}
答案 0 :(得分:4)
template <> class X<1>;
只是专业化的前向声明,并且不传达有关该类型布局的信息。由于main.cpp
(X.cpp
中定义的)实际专业化不可见,因此类型确实不完整。
请记住,类模板特化除了基本名称之外没有共享模板类,因此编译器不知道每个实例在堆栈上分配多少字节(也不是请求的构造函数甚至存在!)除非它知道你在.cpp文件中隐藏的特化的定义。
这类似于执行class Foo;
,然后尝试声明Foo
类型的变量,而不提供类型的定义。
答案 1 :(得分:4)
正如其他人所指出的X<1> x_1();
只是一个函数声明,所以它实际上并没有实例化X<1>
类型的对象。对于incomplete type
错误:您必须将X<1>
的整个声明放入头文件中(不仅仅是正向声明,就像您现在所做的那样)。您可以将实现放在cpp文件中,但是任何使用X<1>
类型的对象(而不仅仅是指向对象的指针)的人(在这种情况下:main
)都必须知道它有多大,它提供了什么方法。
您的困惑可能部分源于您在示例中使用特化的方式:在您的专业化中,唯一与通用模板不同的是其中一个构造函数的定义(所有签名保持不变)。所以你可能认为编译器可以自己解决这个问题。实际上,它不可能这样做,因为您的专用类可能看起来与非专用模板完全不同(具有不同的构造函数,不同的成员/成员函数)。像这样:
template <int I>
struct X {
bool hello(int x, int y);
};
template<>
struct X<1> {
int goodbye(std::string x);
};
如果编译器只看到template<> struct X<1>;
,那该怎么办呢?