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

时间:2015-08-06 14:46:04

标签: c++ types friend

在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声明了一个Screens矢量,但它仍然是一个不完整的类型。我的例子中的这些类实际上是基于C ++ Primer的第7.3章。一个问题要求我定义我自己的Screen和Window_mgr版本,其中成员函数clear是Window_mgr的成员和Screen的朋友。除Window_mgr外,还包含一个Screens矢量。

如果创建一个不完整类型的向量IS无效,我将如何使用前向声明执行此操作?如果我在Window_mgr中有一个屏幕矢量,那么它的类定义必须在类Screen之后定义。除了Screen必须有一个window_mgr的clear成员函数的朋友声明,但是下面的重新排列是一个错误,因为Screen在不完整的类型上使用了scope操作符;

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, ' ');
}

我能想到的唯一方法就是用一个只是朋友的类声明来替​​换memberfunction friend声明

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

但这不是我想要的问题。

2 个答案:

答案 0 :(得分:3)

标准容器目前不支持不完整类型;比照Can standard container templates be instantiated with incomplete types? - 也就是说,实现可以选择支持不完整类型作为扩展,但这会使您的代码不可移植。

论文n4371 Minimal incomplete type support for standard containers, revision 2已纳入C ++标准的最新草稿(n4527)中,因此除非出现意外情况,否则vector很可能会支持不完整类型,C ++ 17中的listforward_list

有一种方法可以满足“C ++ Primer”中的要求,而不依赖于实现扩展或C ++ 17:

  1. clearWindow_mgr;
  2. 的成员函数
  3. Window_mgr::clear()Screen;
  4. 的朋友
  5. Window_mgr包含Screen s:
  6. 的向量

    您可以将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的定义来打破依赖循环。

答案 1 :(得分:3)

简短的回答是,使用不完整类型的向量是非法的。就像任何其他STL容器一样。某些东西编译甚至可能完美无缺,这并不意味着它是合法的。

您在(1)引用this文章中提到的文章。这给出了一个非常清楚(虽然很长)的解释。

正如我解释的那样,它归结为以下几点:

  • 一般情况下,在不完整类型上使用STL模板是非法的
  • 在实践中,它适用于一些STL容器和库
  • Vector就是一个让你真正感到安全使用的案例。我提到的article的注释章节的最后一点是:
  

但也许它应该逐个放宽,并且vector看起来像是这种特殊情况处理的一个很好的候选者:它是一个标准的容器类,有充分的理由用不完整的类型实例化它以及标准库实现者希望使其工作的地方。

您描述的另一个问题是常见问题。为了简化它,您可以说以下内容。假设你有两个班级:

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

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

这似乎是一件理想的事情。但是在标准的限制范围内,这是不可能的。根据标准解决此问题的方法是使用指针列表(std::shared_pointer在这里很有用)。或者,辞职不遵守标准并使用前瞻性声明。几乎任何编译器都可以使用。