无法围绕 std::string 创建仅产生“语法糖”的包装器

San*_*lín 1 c++ inheritance stdstring

我知道 std::string不是为继承而设计的,但是,我想知道为什么这个类定义不能编译:

\n
using std::string;\nclass ExtendedString: public string\n{\npublic:\n    using string::string;\n    ExtendedString left(size_type n) const {\n        return substr(0, n>size()?size():n);\n    }\n};\n
Run Code Online (Sandbox Code Playgroud)\n

我收到此错误:

\n
../../src/capel-tool.cpp:21:17: error: could not convert \xe2\x80\x98std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::substr(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type) const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int](0, ((n > ((const ExtendedString*)this)->ExtendedString::<anonymous>.std::__cxx11::basic_string<char>::size()) ? ((const ExtendedString*)this)->ExtendedString::<anonymous>.std::__cxx11::basic_string<char>::size() : n))\xe2\x80\x99 from \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99 to \xe2\x80\x98ExtendedString\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n

我预计left函数将使用 std::string 中的任何默认构造函数。

\n

即使我添加一个像这样的显式构造函数:

\n
using std::string;\nclass ExtendedString: public string\n{\npublic:\n    using string::string;\n    ExtendedString left(size_type n) const {\n        return substr(0, n>size()?size():n);\n    }\n\n    explicit ExtendedString(const std::string &s);\n};\n
Run Code Online (Sandbox Code Playgroud)\n

我仍然遇到同样的错误。

\n

仅当我添加普通构造函数时:

\n
using std::string;\nclass ExtendedString: public string\n{\npublic:\n    using string::string;\n    ExtendedString(const std::string &s);\n    ExtendedString left(size_type n) const {\n        return substr(0, n>size()?size():n);\n    }\n};\n
Run Code Online (Sandbox Code Playgroud)\n

代码编译正常。

\n

现在想象一下我想对一个专为继承而设计的类做同样的事情。难道我不能以一种类可以互换使用的方式创建这种包装器,而不需要创建构造函数,它将被创建和调用,而不仅仅是“语法糖”

\n

编辑1:

\n

这个建议的问题确实解释了为什么我的代码不起作用,但它没有像已接受的问题那样提出任何替代方案。

\n

Jan*_*tke 5

您的代码的问题是substr返回 a std::string,而不是ExtendedString. 类型之间不存在隐式转换,因此 return 语句无法编译。在继承层次结构中存在隐式转换,即您可以转换ExtendedStringstd::string,但反之则不行。

要仅针对您的left功能解决此问题:

  • 你可以使构造函数隐式:ExtendedString(const std::string&)
  • 显式构造:return ExtendedString(substr(...))

然而,一般来说,您想要做的事情在 C++ 中不太可能实现。您继承的每个成员函数std::string都不会返回ExtendedString,因此您的接口是错误的。C++ 无法让您定义扩展方法,而这是您正在模仿的功能。

“清洁”替代品

  • std::string创建从to 的隐式转换ExtendedString,但这会导致不必要的复制
    • 注意:这是你在声明时所做的ExtendedString(const std::string &s);
  • 私有继承,std::string或者更好的是,将其包装为成员
    • 为每个成员函数创建包装函数std::string,并让它们ExtendedString在必要时获取/返回
  • 只需创建自由函数(见下文)
std::string left(const std::string& s, std::size_t n) {
    return s.substr(0, std::min(s.size(), n));
}
Run Code Online (Sandbox Code Playgroud)

尽管有时打电话str.function()、有时打电话很烦人function(str),但与任何替代方案相比,这种不一致只是一个很小的代价。