我可以使用STL容器管理不完整的类对象吗?

Can I manage incomplete class objects using STL containers?

本文关键字:对象 可以使 STL 管理 我可以      更新时间:2023-10-16

在c++ 11中,声明一个仍然是不完整类型的类的向量是绝对无效的,对吧?我认为我只能使用不完整类型作为指针、引用、返回类型或参数类型。在(1)中搜索"不完整类型的向量",对我来说,不完整类型的容器应该是一个错误(我使用的是g++版本4.8.1)。然而,下面的代码在我的系统上编译得很好:

#ifndef SCREEN
#define SCREEN
#include <string>
using namespace std;
class Screen;
class Window_mgr{
public:
    typedef vector<Screen>::size_type screenindex;
    void clear(screenindex);
private:
    vector<Screen> screens;
};
class Screen{
    friend void Window_mgr::clear(screenindex);
public:
    typedef string::size_type pos;
    Screen() = default;
    Screen(pos ht, pos wt): height(ht), width(wt), contents(ht*wt, ' ') { }
    Screen(pos ht, pos wt, char c): height(ht), width(wt), contents(ht*wt, c) { }
private:
    pos height = 0, width = 0;
    pos cursor = 0;
    string contents;
};
inline void Window_mgr::clear(screenindex i){
    Screen& s = screens[i];
    s.contents = string(s.height*s.width, ' ');
}
#endif // SCREEN

尽管Window_mgr声明了一个screen的向量,它仍然是一个不完整的类型。我的例子中的这些类实际上是基于c++入门第7.3章的。一个问题要求我定义我自己版本的Screen和Window_mgr,其中成员函数clear是Window_mgr的成员,并且是Screen的友元。除了Window_mgr也意味着包含一个屏幕向量。

如果创建一个不完整类型的向量是无效的,我该如何使用前向声明来做到这一点?如果我在Window_mgr中有一个屏幕向量,那么它的类定义必须在Screen类已经定义之后。除了Screen必须有Window_mgr的clear成员函数的友元声明,但下面的重新排列是错误的,因为Screen在不完整类型上使用了作用域操作符;

class Window_mgr;
class Screen{
    friend void Window_mgr::clear(screenindex);
public:
    typedef string::size_type pos;
    Screen() = default;
    Screen(pos ht, pos wt): height(ht), width(wt), contents(ht*wt, ' ') { }
    Screen(pos ht, pos wt, char c): height(ht), width(wt), contents(ht*wt, c) { }
private:
    pos height = 0, width = 0;
    pos cursor = 0;
    string contents;
};
class Window_mgr{
public:
    typedef vector<Screen>::size_type screenindex;
    void clear(screenindex);
private:
    vector<Screen> screens;
};
inline void Window_mgr::clear(screenindex i){
    Screen& s = screens[i];
    s.contents = string(s.height*s.width, ' ');
}

我能想到的唯一方法是将成员函数友元声明替换为类的纯友元声明

class Screen{
friend class Window_mgr;
// other stuff
}

但这不是问题想要我做的。

标准容器目前不支持不完整类型;标准容器模板可以用不完整类型实例化吗?也就是说,实现可以选择支持不完整类型作为扩展,但这会使你的代码不可移植。

论文n4371对标准容器的最小不完整类型支持,修订版2已被纳入最新的c++标准草案(n4527),因此除非出现意外情况,否则很可能在c++ 17中支持vector, listforward_list的不完整类型。


有一种方法可以满足"c++入门"中的要求,而不依赖于实现扩展或c++ 17:

  1. clearWindow_mgr的成员函数;
  2. Window_mgr::clear()Screen的朋友;
  3. Window_mgr is包含Screen s的矢量:

你可以让Screen成为Window_mgr的嵌套类:

class Window_mgr{
public:
    typedef std::size_t screenindex;
    void clear(screenindex);
    class Screen{
        friend void Window_mgr::clear(screenindex);
    public:
        // ...
    };
// ...
private:
    vector<Screen> screens;
};

即使在这里,我也不得不调整类型screenindex的定义来打破依赖循环。

简短的回答是,在不完整类型中使用vector是非法的。就像使用任何其他STL容器一样。某些东西可以编译,甚至可以完美地工作,并不意味着它是合法的。

你在(1)中提到的文章引用了这篇文章。这给了一个非常清楚(尽管很长)的解释。

根据我的解释,它归结为以下内容:

  • 通常在不完整类型上使用STL模板是非法的
  • 在实践中,它与一些STL容器和库一起工作
  • Vector是一种你可以放心使用它的情况。我提到的文章的注释章节的最后一点是:

但是也许它应该在具体情况的基础上放松,vector看起来像是这种特殊情况处理的良好候选:它是一个标准容器类,有很好的理由用不完整类型实例化它,并且标准库实现者希望使它工作。

你描述的另一个问题很常见。为了简化它,你可以这样说。假设你有两个类:

class Foo
{
   std::vector<Bar> myVector;
}
class Bar
{
   std::vector<Foo> myVector;
}

这似乎是一个合理的要求。但在标准的限制下,这是不可能的。根据标准解决这个问题的一种方法是使用指针列表(std::shared_pointer在这里很有用)。或者,放弃不遵循标准而使用前向声明。