不能创建一个包装std::string的包装器,它只会产生”syntax sugar”。

huangapple go评论70阅读模式
英文:

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&gt;size()?size():n);
	}
};

I get this error:

../../src/capel-tool.cpp:21:17: error: could not convert ‘std::__cxx11::basic_string&lt;_CharT, _Traits, _Alloc&gt;::substr(std::__cxx11::basic_string&lt;_CharT, _Traits, _Alloc&gt;::size_type, std::__cxx11::basic_string&lt;_CharT, _Traits, _Alloc&gt;::size_type) const [with _CharT = char; _Traits = std::char_traits&lt;char&gt;; _Alloc = std::allocator&lt;char&gt;; std::__cxx11::basic_string&lt;_CharT, _Traits, _Alloc&gt;::size_type = long unsigned int](0, ((n &gt; ((const ExtendedString*)this)-&gt;ExtendedString::&lt;anonymous&gt;.std::__cxx11::basic_string&lt;char&gt;::size()) ? ((const ExtendedString*)this)-&gt;ExtendedString::&lt;anonymous&gt;.std::__cxx11::basic_string&lt;char&gt;::size() : n))’ from ‘std::__cxx11::basic_string&lt;char&gt;’ 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&gt;size()?size():n);
	}

	explicit ExtendedString(const std::string &amp;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 &amp;s);
	ExtendedString left(size_type n) const {
		return substr(0, n&gt;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::stringExtendedString 的隐式转换,但这会导致不必要的复制
    • 注意:这就是你声明 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&amp;)
  • 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 to ExtendedString, but this results in unnecessary copying
    • note: this is what you've done when declaring ExtendedString(const std::string &amp;s);
  • 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/return ExtendedString where necessary
  • just create free functions (see below)
std::string left(const std::string&amp; 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&amp; s) : data_(s) {}

    ExtendedString left(std::string::size_type n) const {
        return ExtendedString(data_.substr(0, n &gt; data_.size() ? data_.size() : n));
    }

    // Forward other methods as needed
};

huangapple
  • 本文由 发表于 2023年6月25日 21:20:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76550612.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定