创建显式专用的模板类对象会产生“对象具有初始化程序但不完整类型”错误

时间:2014-10-13 15:05:30

标签: c++ template-specialization

简单的案例。我不太明白为什么括号是调用显式实例化模板的默认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
}

2 个答案:

答案 0 :(得分:4)

template <> class X<1>;只是专业化的前向声明,并且不传达有关该类型布局的信息。由于main.cppX.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>;,那该怎么办呢?