【问题标题】:forward declaration of std::string and std::wstringstd::string 和 std::wstring 的前向声明
【发布时间】:2014-06-21 23:22:11
【问题描述】:

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

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?

【问题讨论】:

  • 为什么需要前向声明它们? #include &lt;string&gt; 可以很好地处理任何声明。
  • 这是我要避免的 - #include &lt;string&gt;
  • 你为什么要避免#include?还有其他方法可以提高编译速度;看看预编译的头文件。
  • 作为经验法则。如果它有模板:很难转发声明。如果您从模板类继承,它不会使前向声明更容易。一般来说,stl 定义过于松散,无法安全地向前声明。
  • 您不知道英特尔编译器是否支持预编译头文件...。您查看文档了吗?或者问谷歌?看起来 pch 支持在那里,而不是新的。过去十年的任何版本都会有它。

标签: c++ templates inheritance forward-declaration stdstring


【解决方案1】:

对于 c++ 标准使用继承而不是 typedef [...]

没有。

std::basic_string 并不意味着以任何形式继承。此限制允许实现 std::basic_string,因为它没有虚函数表,所以创建和销毁更便宜(更快)。

如果您需要定义 std::[w]string,只需 #include 即可。

编辑(回复评论)

C++(和标准库)的指导原则之一是“不为不使用的东西付费”。这意味着代码是以这样一种方式编写的,您不应该为不需要的功能产生运行时成本。

如果每个实例都有一个虚拟表,则创建一个 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)。

【讨论】:

  • 只有至少定义了一个虚函数才存在虚函数表
  • 这是正确的,但是因为在继承的情况下,您需要一个虚拟析构函数(以避免进入 UB),允许继承 std::basic_string,在实践这意味着至少“使用虚拟析构函数定义类”。
  • > std::basic_string 并不意味着以任何形式继承。您能对此提供参考或解释吗?不是“仅仅因为”
  • @utnapistim:不,继承在每种情况下都不需要虚拟析构函数。仅在需要多态删除时。
  • @BenVoigt,技术上是的,但这意味着特化不会实现多态删除。从库编写者的角度来看,对客户端代码施加这样的要求(比“不继承”)要困难得多。最重要的是,没有人期望它(因此您正在增加代码的“WTF / SLOC”比率)。
【解决方案2】:

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

想象一下,我有一个功能应该适用于两个版本:

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

我需要做的事情包括调用第 3 方函数 bar,它作为 std::stringstd::wstring 的重载存在,但不是作为模板:

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

那么,如果你有一个真正的 typedef,那就行了。如果你改用继承,它要么编译失败,要么默默地创建字符串的副本以传递给bar,这是一个隐藏的性能陷阱。

或者取substr 成员函数。它返回与调用时相同的类型,但这意味着如果它是basic_string 的成员,则如果它们不是真正的typedef,它不会返回stringwstring。这会导致各种微妙的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-12
    • 1970-01-01
    • 2011-09-11
    相关资源
    最近更新 更多