英文:
Can not create a wrapper around std::string that results in only "syntax sugar"
问题
我知道 std::string 不是为继承而设计,然而,我想知道为什么这个类定义无法编译:
using std::string;
class ExtendedString: public string
{
public:
using string::string;
ExtendedString left(size_type n) const {
return substr(0, n>size()?size():n);
}
};
我收到了这个错误:
../../src/capel-tool.cpp:21:17: 错误:无法将‘std::__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))’ 从 ‘std::__cxx11::basic_string<char>’ 转换为 ‘ExtendedString’
我期望 left
函数会使用 std::string 中的任何默认构造函数。
即使我添加一个显式构造函数如下:
using std::string;
class ExtendedString: public string
{
public:
using string::string;
ExtendedString left(size_type n) const {
return substr(0, n>size()?size():n);
}
explicit ExtendedString(const std::string &s);
};
我仍然收到相同的错误。
只有当我添加一个普通构造函数时:
using std::string;
class ExtendedString: public string
{
public:
using string::string;
ExtendedString(const std::string &s);
ExtendedString left(size_type n) const {
return substr(0, n>size()?size():n);
}
};
代码才能编译通过。
现在想象一下,我想要对一个设计为继承的类做同样的事情。我不能只是创建这种包装器,使得这些类可以互换使用,而无需创建构造函数,这样它们只会被称为而不是只是“语法糖”。
编辑 1:
这个建议的问题确实解释了我的代码为什么不能工作,但它并没有提出任何替代方案,就像被接受的问题所做的那样。
英文:
I know that std::string is not designed for inheritance, however, I wonder why this class definition doesn't compile:
using std::string;
class ExtendedString: public string
{
public:
using string::string;
ExtendedString left(size_type n) const {
return substr(0, n>size()?size():n);
}
};
I get this error:
../../src/capel-tool.cpp:21:17: error: could not convert ‘std::__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))’ from ‘std::__cxx11::basic_string<char>’ to ‘ExtendedString’
I expected that the left
function would use any default constructor from std::string.
Even if I add an explicit constructor like this:
using std::string;
class ExtendedString: public string
{
public:
using string::string;
ExtendedString left(size_type n) const {
return substr(0, n>size()?size():n);
}
explicit ExtendedString(const std::string &s);
};
I still get the same error.
Only when I add a normal constructor:
using std::string;
class ExtendedString: public string
{
public:
using string::string;
ExtendedString(const std::string &s);
ExtendedString left(size_type n) const {
return substr(0, n>size()?size():n);
}
};
The code compiles ok.
Now imagine I want to make the same with a class that is designed for inheritance. Can not I just create this kind of wrapper in a way that the classes can be used interchangeablily without the need to create constructors thad would be created and called instead of being only "syntax sugar"
EDIT 1:
This suggested question does explain why my code does not work, but it does not propose any alternatives, as the accepted question does.
答案1
得分: 5
问题出在你的代码中,substr
返回的是一个 std::string
,而不是 ExtendedString
。这两种类型之间没有隐式转换,因此那个返回语句无法编译通过。
在继承层次结构中存在向上的隐式转换,即你可以将 ExtendedString
转换为 std::string
,但反过来不行。
为了解决你的 left
函数的问题:
- 你可以将构造函数设置为隐式:
ExtendedString(const std::string&)
- 显式构造:
return ExtendedString(substr(...))
然而,总体来说,在C++中,你尝试做的事情并不是一种优雅的方式。你从 std::string
继承的每个成员函数都不会返回 ExtendedString
,所以你的接口是错误的。C++ 不提供定义 扩展方法 的方式,这是你正在模仿的功能。
"干净" 的替代方法
- 创建一个从
std::string
到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));
}
尽管有时需要调用 str.function()
和 function(str)
,但与任何其他替代方法相比,这种不一致性是一个小代价。
英文:
The problem with your code is that substr
returns a std::string
, not an ExtendedString
. No implicit conversion exists between the types, so that return statement cannot compile.
There is an implicit conversion up the inheritance hierarchy, i.e. you can convert ExtendedString
to std::string
, but not the other way around.
To solve this for just your left
function:
- you could make the constructor implicit:
ExtendedString(const std::string&)
- construct explicitly:
return ExtendedString(substr(...))
However, in general, what you're trying to do is just not nicely possible in C++. Every member function that you inherit from std::string
is not going to return ExtendedString
, so you have the wrong interface. C++ gives you no way of defining extension methods, which is the feature you're imitating.
"Clean" Alternatives
- create an implicit conversion from
std::string
toExtendedString
, but this results in unnecessary copying- note: this is what you've done when declaring
ExtendedString(const std::string &s);
- note: this is what you've done when declaring
- inherit privately from
std::string
or better yet, wrap it as a member- create wrapper functions for every member function that
std::string
has, and make them take/returnExtendedString
where necessary
- create wrapper functions for every member function that
- just create free functions (see below)
std::string left(const std::string& s, std::size_t n) {
return s.substr(0, std::min(s.size(), n));
}
As annoying as it is to sometimes call str.function()
and sometimes function(str)
, that inconsistency is a small price compared to any alternative.
答案2
得分: -1
class ExtendedString
{
private:
std::string data_;
public:
ExtendedString() = default;
ExtendedString(const std::string& s) : data_(s) {}
ExtendedString left(std::string::size_type n) const {
return ExtendedString(data_.substr(0, n > data_.size() ? data_.size() : n));
}
// Forward other methods as needed
};
英文:
class ExtendedString
{
private:
std::string data_;
public:
ExtendedString() = default;
ExtendedString(const std::string& s) : data_(s) {}
ExtendedString left(std::string::size_type n) const {
return ExtendedString(data_.substr(0, n > data_.size() ? data_.size() : n));
}
// Forward other methods as needed
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论