代码之家  ›  专栏  ›  技术社区  ›  xyzzyrz

为什么C++模板让我绕过不完整的类型(前向声明)?

  •  1
  • xyzzyrz  · 技术社区  · 15 年前

    我尝试了以下简单程序的三次迭代。这是一个高度简化的尝试,试图编写一个容器和迭代器类对,但我遇到了不完整类型(正向声明)的问题。我发现,一旦我将所有内容都模板化,这实际上是可能的——但前提是我实际使用了模板参数!(我通过观察 Google sparsetable code )

    有什么提示可以解释为什么第二个有效,而第三个无效?(我知道第一个不起作用的原因——编译器需要知道容器的内存布局。)

    事先谢谢。

    // This doesn't work: invalid use of incomplete type.
    #if 0
    struct container;
    struct iter {
      container &c;
      int *p;
      iter(container &c) : c(c), p(&c.value()) {}
    };
    struct container {
      int x;
      int &value() { return x; }
      iter begin() { return iter(*this); }
    };
    int main() {
      container c;
      c.begin();
      return 0;
    }
    #endif
    
    // This *does* work.
    template<typename T> struct container;
    template<typename T> struct iter {
      container<T> &c;
      T *p;
      iter(container<T> &c) : c(c), p(&c.value()) {}
    };
    template<typename T> struct container {
      T x;
      T &value() { return x; }
      iter<T> begin() { return iter<T>(*this); }
    };
    int main() {
      container<int> c;
      c.begin();
      return 0;
    };
    
    // This doesn't work either.
    #if 0
    template<typename T> struct container;
    template<typename T> struct iter {
      container<int> &c;
      int *p;
      iter(container<int> &c) : c(c), p(&c.value()) {}
    };
    template<typename T> struct container {
      int x;
      int &value() { return x; }
      iter<int> begin() { return iter<int>(*this); }
    };
    int main() {
      container<int> c;
      c.begin();
      return 0;
    }
    #endif
    
    3 回复  |  直到 15 年前
        1
  •  4
  •   dirkgently    15 年前

    第一个要求定义 container 因为你在做复制操作。如果定义的构造函数 iter 之后 容器 你会没事的。所以:

    struct container;
    struct iter {
      container &c;
      int *p;
      iter(container &c);
    };
    
    struct container {
      int x;
      int &value() { return x; }
      iter begin() { return iter(*this); }
    };
    
    iter::iter(container &c) : c(c), p(&c.value()) {}
    
    int main() {
      container c;
      c.begin();
      return 0;
    }
    

    第二个示例之所以有效,是因为在您的 main 功能。到那时,所有类型都已定义。尝试移动任何 伊特尔 容器 模板定义在main之后,您将遇到一个错误。

    第三个例子是 int 似乎是这样。这应该编译,因为 伊特尔 不使用。您对专门化语法有点了解。但是,没有合适的构造函数,因此您只能为 x . 此外,迭代器由指针很好地建模。经过 this 的价值不会有多大帮助。迭代器通常是序列而不是单个对象所必需的。但是,没有什么能阻止你去建造一个。

    你不需要 ; 在函数体之后。

        2
  •  2
  •   Paul    15 年前

    通过在定义容器后定义iter::iter(),可以在没有模板的情况下完成此操作:

    struct container;
    
    struct iter {
      container &c;
      int *p;
      iter(container &c);
    };
    
    struct container {
      int x;
      int &value() { return x; }
      iter begin() { return iter(*this); }
    };
    
    iter::iter(container &c)
        : c(c), p(&c.value()) {}
    
    int main() {
      container c;
      c.begin();
      return 0;
    }
    

    模板版本可以工作,因为当您实例化模板时,两个类都是完全定义的。

        3
  •  0
  •   BatchyX    15 年前

    在第一种情况下,您试图在类被定义之前访问容器类的成员函数,因此这不起作用。

    在第二种情况下,模板在第一次与特定类型一起使用时被实例化。在这一点上,容器类已经在main中定义,因此它可以编译。

    在第三种情况下,有一个循环引用。容器使用ITER,ITER使用容器,因此无法工作。