我可以使用 STL 容器管理不完整的 class 对象吗?
Can I manage incomplete class objects using STL containers?
在 C++11 中,声明一个 class 的向量肯定是无效的,它仍然是一个不完整的类型,对吧?我以为我只能使用不完整的类型作为指针、引用、return 类型或参数类型。在 (1) 中搜索 "vector of incomplete type" 向我建议不完整类型的容器应该是错误的(我使用的是 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 向量,它仍然是一个不完整的类型。我示例中的这些 classes 实际上基于 C++ Primer 的第 7.3 章。一个问题要求我定义我自己的 Screen 和 Window_mgr 版本,其中成员函数 clear 是 Window_mgr 的成员和 Screen 的友元。除了 Window_mgr 还意味着包含屏幕向量。
如果创建一个不完整类型的向量是无效的,我该如何使用前向声明来做到这一点?如果我在 Window_mgr 中有一个屏幕矢量,那么它的 class 定义必须在 class 屏幕已经定义之后。除了Screen must have a friend declaration of the clear member function of Window_mgr,但是下面的重排是错误的,因为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
的友元声明替换成员函数友元声明
class Screen{
friend class Window_mgr;
// other stuff
}
但这不是我想要的问题。
标准容器目前不支持不完整的类型;比照。 Can standard container templates be instantiated with incomplete types? - 也就是说,实现可以选择支持不完整的类型作为扩展,但这会使您的代码不可移植。
C++ 标准的论文 n4371 Minimal incomplete type support for standard containers, revision 2 has been incorporated into the most recent draft (n4527),因此除非出现意外情况,否则 vector
、list
和 forward_list
很可能会支持不完整的类型在 C++17 中。
有一种方法可以满足 "C++ Primer" 中的要求,而不依赖于实现扩展或 C++17:
clear
是Window_mgr
的成员函数;
Window_mgr::clear()
是 Screen
的朋友;
Window_mgr
包含一个向量 Screen
s:
您可以使 Screen
成为 Window_mgr
的嵌套 class:
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
的定义以打破依赖循环。
简短的回答是,使用不完整类型的向量是非法的。就像任何其他 STL 容器一样。某些东西可以编译甚至可以完美运行的事实并不意味着它是合法的。
您在 (1) references this 篇文章中提到的文章。这对此事给出了非常清楚(尽管很长)的解释。
按照我的解释,它归结为以下几点:
- 一般来说,在不完整的类型上使用 STL 模板是非法的
- 实际上它适用于一些 STL 容器和库
- Vector 是您真正可以放心使用它的一种情况。我提到的 article 注释章节的最后一点指出:
But perhaps it should be relaxed on a case-by-case basis, and vector looks like a good candidate for such special-case treatment: it's the one standard container class where there are good reasons to instantiate it with an incomplete type and where Standard Library implementors want to make it work.
您描述的另一个问题很常见。为了简化它,您可以说以下内容。假设你有两个 类:
class Foo
{
std::vector<Bar> myVector;
}
class Bar
{
std::vector<Foo> myVector;
}
这似乎是一件合理的事情。但在标准的限制下,这简直是不可能的。根据标准解决这个问题的一种方法是使用指针列表(std::shared_pointer
在这里很有用)。或者,放弃不遵守标准并使用前向声明。对于几乎任何编译器,这都可以工作。
在 C++11 中,声明一个 class 的向量肯定是无效的,它仍然是一个不完整的类型,对吧?我以为我只能使用不完整的类型作为指针、引用、return 类型或参数类型。在 (1) 中搜索 "vector of incomplete type" 向我建议不完整类型的容器应该是错误的(我使用的是 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 向量,它仍然是一个不完整的类型。我示例中的这些 classes 实际上基于 C++ Primer 的第 7.3 章。一个问题要求我定义我自己的 Screen 和 Window_mgr 版本,其中成员函数 clear 是 Window_mgr 的成员和 Screen 的友元。除了 Window_mgr 还意味着包含屏幕向量。
如果创建一个不完整类型的向量是无效的,我该如何使用前向声明来做到这一点?如果我在 Window_mgr 中有一个屏幕矢量,那么它的 class 定义必须在 class 屏幕已经定义之后。除了Screen must have a friend declaration of the clear member function of Window_mgr,但是下面的重排是错误的,因为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
的友元声明替换成员函数友元声明class Screen{
friend class Window_mgr;
// other stuff
}
但这不是我想要的问题。
标准容器目前不支持不完整的类型;比照。 Can standard container templates be instantiated with incomplete types? - 也就是说,实现可以选择支持不完整的类型作为扩展,但这会使您的代码不可移植。
C++ 标准的论文 n4371 Minimal incomplete type support for standard containers, revision 2 has been incorporated into the most recent draft (n4527),因此除非出现意外情况,否则 vector
、list
和 forward_list
很可能会支持不完整的类型在 C++17 中。
有一种方法可以满足 "C++ Primer" 中的要求,而不依赖于实现扩展或 C++17:
clear
是Window_mgr
的成员函数;Window_mgr::clear()
是Screen
的朋友;Window_mgr
包含一个向量Screen
s:
您可以使 Screen
成为 Window_mgr
的嵌套 class:
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
的定义以打破依赖循环。
简短的回答是,使用不完整类型的向量是非法的。就像任何其他 STL 容器一样。某些东西可以编译甚至可以完美运行的事实并不意味着它是合法的。
您在 (1) references this 篇文章中提到的文章。这对此事给出了非常清楚(尽管很长)的解释。
按照我的解释,它归结为以下几点:
- 一般来说,在不完整的类型上使用 STL 模板是非法的
- 实际上它适用于一些 STL 容器和库
- Vector 是您真正可以放心使用它的一种情况。我提到的 article 注释章节的最后一点指出:
But perhaps it should be relaxed on a case-by-case basis, and vector looks like a good candidate for such special-case treatment: it's the one standard container class where there are good reasons to instantiate it with an incomplete type and where Standard Library implementors want to make it work.
您描述的另一个问题很常见。为了简化它,您可以说以下内容。假设你有两个 类:
class Foo
{
std::vector<Bar> myVector;
}
class Bar
{
std::vector<Foo> myVector;
}
这似乎是一件合理的事情。但在标准的限制下,这简直是不可能的。根据标准解决这个问题的一种方法是使用指针列表(std::shared_pointer
在这里很有用)。或者,放弃不遵守标准并使用前向声明。对于几乎任何编译器,这都可以工作。