嵌套类声明:模板与非模板外部类

时间:2014-05-30 21:42:49

标签: c++ templates nested-class forward-declaration

我有一个c ++模板类,里面有一个嵌套类,类似于:

template<int d>
class Outer_t
{
public:
    class Inner;

    Inner i;
};

template<int d>
class Outer_t<d>::Inner
{
public:
    float x;
};

int main ()
{
    Outer_t<3> o_t; // 3 or any arbitrary int
    o_t.i.x = 1.0;

  return 0;
}

这个编译没有任何问题。但是,只要我声明一个类似的非模板类,就像这样:

class Outer_1
{
public:
    class Inner;

    Inner i;
};

class Outer_1::Inner
{
public:
    float x;
};

int main ()
{
    Outer_1 o1;
    o1.i.x = 1.0;

  return 0;
}

我开始收到以下错误(我使用的是gcc 4.6.3):&#34;错误:字段'i'的类型不完整&#34;。我知道我可以通过在外部类中定义内联类内联来解决这个问题:

class Outer_2
{
public:
    class Inner {
    public:
        float x;
    };

    Inner i;
};

这将编译,但我想避免定义嵌套类内联。所以我有两个问题:模板与非模板嵌套类声明之间存在明显奇怪差异的原因是什么?是否有一种优雅的方式来声明和使用外部类中的nester类,同时避免将其定义为内联,与模板类的风格大致相同?在此先感谢您的帮助!

2 个答案:

答案 0 :(得分:4)

为什么编译错误?

编译器需要知道成员变量类型的大小才能进行对象布局。对于Outer_1,因为Inner只是一个前瞻性声明,我们不知道它的大小。因此编译错误。但是对于Outer_2,定义是内联的,所以当我们到达Inner类型的成员变量时,我们知道它的大小。

为什么模板会通过?

另一方面,

类模板在模板实例化发生之前无法定义。在您的示例中,隐式实例化发生在main中。此时,Inner的定义已完成,因此其大小已知。我们可以看到,在Inner的定义之前使用显式实例化就是这种情况。

 template<int d>
 class Outer_t
 {
 public:
     class Inner;

     Inner i;
 };

 template class Outer_t<3>;  // explicit instantation

 template<int d>
 class Outer_t<d>::Inner
 {
 public:
     float x;
 };

 int main ()
 {
     Outer_t<3> o_t; // 3 or any arbitrary int
     o_t.i.x = 1.0;

   return 0;
 }

Clang产生以下错误:

 a.cc:7:11: error: implicit instantiation of undefined member 'Outer_t<3>::Inner'
     Inner i;
           ^
 a.cc:10:16: note: in instantiation of template class 'Outer_t<3>' requested here
 template class Outer_t<3>;
           ^
 a.cc:5:11: note: member is declared here
     class Inner;
           ^

解决方案

如果你想提取嵌套类的定义,我建议的解决方案是让它像Outer_t一样进行模板化,但为方便起见提供别名。

 template <typename Dummy = void>
 class Outer_1_Impl {
   public:

   class Inner;

   Inner i;
 };

 template <typename Dummy>
 class Outer_1_Impl<Dummy>::Inner {
   public:
   float x;
 };

 using Outer_1 = Outer_1_Impl<>;

 int main () {
   Outer_1 o1;
   o1.i.x = 1.0;
 }

答案 1 :(得分:1)

在定义类时,对象中只能包含具有完整定义的非静态成员。在您的非模板示例中,Outer_1::Inner显然不完整,因为它仅在目前为止声明。创建Inner成员的唯一方法就是像Outer_2一样定义它。

现在,为什么使用类模板时不会出现问题?答案是:当您在Outer_t中实例化main()时,实际上 可以访问Outer_t<T>::Inner的完整定义! ...并且在使用某种类型Outer_t<T>实例化T之前,Outer_t的定义并非真正需要。