C ++模板实例化的时机

时间:2012-02-29 00:32:30

标签: c++

我想知道是否有人知道为什么以下两段代码表现得非常不同。我能理解为什么第二个不起作用,但为什么第一个起作用呢?在同一个地方int x = gc.f();,模板应该被实例化,因此会出现相同的错误,但为什么实际上没有错误?

a.cpp

#include <iostream>

using namespace std;

template <typename T>
struct A {
    struct B {
    };
};

template <typename T>
struct C {
    typedef A<C<T> > D;

    int f() {
        typename D::B p;
        return 0;
    }
};

C<float> gc;
int x = gc.f();

template <typename T>
struct A<C<T> > {
    struct B {
        B() {
            cout << "B::B()" << endl;
        }

        ~B() {
            cout << "B::~B()" << endl;
        }
    };
};

int main() {
}

输出

B::B()
B::~B()

a2.cpp

#include <iostream>

using namespace std;

template <typename T>
struct A {
    struct B {
    };
};

struct C {
    typedef A<C> D;

    int f() {
        D::B p;
        return 0;
    }
};

C gc;
int x = gc.f();

template <>
struct A<C> {
    struct B {
        B() {
            cout << "B::B()" << endl;
        }

        ~B() {
            cout << "B::~B()" << endl;
        }
    };
};

int main() {
}

编译器错误

a2.cpp:24: error: specialization of ‘A<C>’ after instantiation
a2.cpp:24: error: redefinition of ‘struct A<C>’
a2.cpp:6: error: previous definition of ‘struct A<C>’

3 个答案:

答案 0 :(得分:3)

你实际上要求两个不同的东西,虽然两者都与模板实例化有关。

  

为什么第一段代码会编译?

标准规定模板的实际实例化是在处理整个翻译单元后执行的,这意味着模板的真实实例化将毕竟是即使实例化点在翻译单元中更早且更早,该翻译单元中定义的类型也是完整的。有关此内容的更多内容question

  

为什么第二个例子没有编译?

第二个示例的问题是标准要求在第一次使用该特化之前必须声明模板的特化。

  

§14.7.3p6(C ++ 03)

     

如果一个模板,一个成员模板或一个类模板的成员被明确地专门化,那么该特化应该在第一次使用该特化之前声明,这将导致隐式实例化发生在每个翻译单元中。发生了这种用途;无需诊断。

请注意,这里有两个不同的概念。 实例化指的是实例化发生的代码中的 where ,而实例化时。在您的示例中,实例化点是表达式C<float> gc;,而在所有其他情况下在处理完整个翻译单元后 < / em>的

答案 1 :(得分:2)

您只需删除不必要的typename限定符并重新排列一些内容。如错误消息所示,在A<C>中已经实例化之后,您已经专门设置了C。要解决此问题,您可以将A<C>的特化移到C的定义之前,然后转发声明C以消除未声明的标识符错误。

这是固定代码:

#include <iostream>

using namespace std;

template <typename T>
struct A {
    struct B {
    };
};

struct C; // Forward declaration of C

template <>
struct A<C> {
    struct B {
        B() {
            cout << "B::B()" << endl;
        }

        ~B() {
            cout << "B::~B()" << endl;
        }
    };
};

struct C {
    typedef A<C> D;

    int f() {
        D::B p;
        return 0;
    }
};

C gc;
int x = gc.f();


int main() {
}

答案 2 :(得分:1)

因为typename仅用于在模板中指示类型时使用。