std::string和std::wstring的前置声明

13

无法前向声明std::string和std::wstring的问题经常被讨论。据我了解,原因是这些类型是模板类basic_string的实例化的typedef:

namespace std {
  typedef basic_string<char>    string;
  typedef basic_string<wchar_t> wstring;
}

语言不允许typedef的前向声明。

在C ++标准中,使用继承代替typedef是否更好呢:

namespace std {
  class string : public basic_string<char> {};
  class wstring : public basic_string<wchar_t> {};
}

这样我们就可以前向声明 std::string 和 std::wstring 了吗?


4
为什么需要前向声明它们?#include <string>可以很好地处理任何声明。 - JAB
2
这就是我试图避免的事情 - #include <string> - Daniel Heilper
2
你为什么要避免使用 #include 呢?还有其他方法可以提高编译速度,可以看看预编译头文件。 - Ben Voigt
3
一般而言,如果有模板,则很难进行前向声明。即使继承自模板类,也不会使前向声明变得更容易。总的来说,“STL”过于松散定义,不能安全地进行前向声明。作为经验法则。 - Alexander Oh
1
你不知道Intel编译器是否支持预编译头文件... 你查看了文档吗?或者问问谷歌?看起来pch支持已经存在,而且并不是新的。过去十年中的任何版本都将具有此功能。 - Ben Voigt
显示剩余2条评论
2个回答

8
使用继承而不是 typedef 是否更适合 C++ 标准?
不是的。
std::basic_string 没有意图以任何形式被继承。这个限制使得实现 std::basic_string 更便宜(更快),因为它没有虚函数表。
如果你需要定义 std::[w]string,只需包含它即可。
编辑(回答评论)
C++(和标准库)的一个指导原则是“不为你不使用的东西付费”。这意味着代码应该这样编写,即你不应为不需要的功能而产生运行时成本。
如果每个 std::string 实例都有一个虚表,那么创建 std::string 实例的成本将会更高(这将使得在性能关键代码中使用 std::string 变得禁止)。
相反,std::string 旨在作为 C 的 char* 机制的快速、类型安全、RAII 实现。同样,你也不应尝试从 std::vector、list、map、shared_ptr、unique_ptr 等继承。
如果你真的需要一个字符串基类,请考虑自己编写 your_namespace::[w]string_base(一个微不足道的实现是将 std::[w]string 封装在内部)。

12
只有定义了至少一个虚函数,才会存在虚函数表。 - Daniel Heilper
没错,但是在继承的情况下,你 需要 一个虚析构函数(以避免进入 UB),让 std::basic_string 可以被继承,在实践中,这意味着至少要 '定义一个具有虚析构函数的类'。 - utnapistim
std::basic_string并不是用来以任何形式继承的。您能否给出一个参考或解释,而不仅仅是“因为这样”? - Daniel Heilper
14
不需要在每种情况下都为继承使用虚析构函数,只有在需要多态删除时才需要。 - Ben Voigt
2
@BenVoigt,从技术上讲是的,但这意味着特化将不会实现多态删除。对于库编写者来说,这种要求在客户端代码上的实施要困难得多(比“不继承”更困难)。此外,没有人期望它(因此您正在增加代码的“WTF / SLOC”比率)。 - utnapistim

3

“typedef派生类”并不等同于真正的typedef。

想象一下,我有一个函数应该能够与这两个版本一起使用:

template <typename Ch>
void foo(const std::basic_string<Ch>& s) {
  // do some stuff here
}

我需要做的事情包括调用第三方函数bar,它存在于std::stringstd::wstring的重载中,但并不是一个模板:

void bar(const std::string& s) {}
void bar(const std::wstring& s) {}

如果您使用真正的typedef,那么它将可以正常工作。如果使用继承,它将无法编译通过或者悄悄地创建字符串的副本传递给bar函数,这会导致性能陷阱。

再看substr成员函数,它返回与所调用对象相同的类型,但如果它是basic_string的成员,它不会返回stringwstring,除非这些都是真正的typedef。这将导致各种微妙的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接