英文:
adding member function substr for std::span<T> which imitates the string_view::substr
问题
在我的代码中,我想要创建一个名为Span
的新类,通过扩展std::span
类来实现,但新的Span
类具有一些额外的成员函数,这些函数在std::span
中不存在。
其中一个是将remove_prefix
函数添加到std::span
中,该函数已经实现并正常工作。有关详细信息,请参阅我的先前的问题:如何从std::span<T>中删除第一个元素?
现在,我想要添加另一个名为substr
的成员函数(我试图模仿std::string_view
的一些成员函数,因为我需要在我的设计中支持std::span<T>
和std::string_view
两者)。在旧代码中,我只支持std::string_view
,因此有一些代码片段类似于sv.substr()
,现在我希望sv
可以更通用,这意味着它可以是std::string_view
或std::span<T>
(Span<T>
)。
以下是我用来解决这个问题的最小示例代码:
#include <iostream>
#include <span>
using namespace std;
// Span class which is derived class from std::span
// add remove_prefix and substr function which is similar like std::string_view
template<typename T>
class Span : public std::span<T>
{
public:
// Inheriting constructors from std::span
using std::span<T>::span;
// add a public function which is similar to std::string_view::remove_prefix
constexpr void remove_prefix(std::size_t n) {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
// add another public function substr, which is the span::subspan
constexpr Span substr(std::size_t pos, std::size_t count){
auto& self = static_cast<std::span<T>&>(*this);
auto ret = self.subspan(pos, count);
return static_cast<Span<T>&>(ret);
}
};
float arr[3] = {1.0, 2.0, 3.0};
int main()
{
Span<float> a(arr, 2);
Span<float> b = a.substr(0, 1);
return 0;
}
上述代码在具有C++20支持的g++下可以构建和运行。我的问题是:我的实现是否正确?因为我在代码中使用了许多static_cast
。实际上,std::span
已经有一个名为subspan
的函数,我所做的只是创建了一个函数名称别名substr
。
有任何想法吗?谢谢。
英文:
In my code, I would like to create a new class named Span
by extending the std::span
class, but the new Span
has some extra member functions which does not exist in std::span
.
One is adding the remove_prefix
function to std::span
, which is implemented and works OK. See my previous question for details: what is the way to remove the first element from a std::span<T>?
Now I would like to add another member function named substr
(I try to imitates some member functions of std::string_view
because I need to support both std::span<T>
and std::string_view
in my design). In the old code, I have only std::string_view
support, so it has some code snippet like sv.substr()
, now I would like sv
could be general, which means it could be either std::string_view
or std::span<T>
(Span<T>
).
Here is the minimal sample code I used to solve this issue:
#include <iostream>
#include <span>
using namespace std;
// Span class which is derived class from std::span
// add remove_prefix and substr function which is similar like std::string_view
template<typename T>
class Span : public std::span<T>
{
public:
// Inheriting constructors from std::span
using std::span<T>::span;
// add a public function which is similar to std::string_view::remove_prefix
constexpr void remove_prefix(std::size_t n) {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
// add another public function substr, which is the span::subspan
constexpr Span substr(std::size_t pos, std::size_t count){
auto& self = static_cast<std::span<T>&>(*this);
auto ret = self.subspan(pos, count);
return static_cast<Span<T>&>(ret);
}
};
float arr[3] = {1.0, 2.0, 3.0};
int main()
{
Span<float> a(arr, 2);
Span<float> b = a.substr(0, 1);
return 0;
}
The above code builds and runs OK under g++ with C++20 support. My question is: is my implementation correct? Because I use so many static_cast
in the code. In-fact, std::span
already has a function named subspan
, what I do is just make a function name alias substr
.
Any ideas? Thanks.
答案1
得分: 2
你的 remove_prefix
实现没有问题,但可以简化为:
constexpr void remove_prefix(std::size_t n) {
*this = subspan(n);
}
你的 substr
实现有问题,因为你将一个 lvalue 引用强制转换为一个其最终派生类型为 std::span<T>
而不是 Span<T>
的对象。你可以修复它:
constexpr Span substr(std::size_t pos, std::size_t count){
return subspan(pos, count);
}
不过,我建议不这样做。我建议修改调用代码以使用你已经为 std::span<T>
和 std::string_view
等类型重载的自由函数:
template<typename T>
constexpr void remove_prefix(std::span<T> & span, std::size_t n) {
span = span.subspan(n);
}
template<typename CharT, typename Traits>
constexpr void remove_prefix(std::basic_string_view<CharT, Traits> & view, std.size_t n) {
view.remove_prefix(n);
}
template<typename T>
constexpr std::span<T> substr(std::span<T> span, std::size_t n) {
return span.subspan(n);
}
template<typename CharT, typename Traits>
constexpr std::basic_string_view<CharT, Traits> substr(std::basic_string_view<CharT, Traits> view, std::size_t n) {
return view.substr(n);
}
英文:
Your implementation of remove_prefix
isn't faulty, but it can be simplified to
constexpr void remove_prefix(std::size_t n) {
*this = subspan(n);
}
Your implementation of substr
is faulty, because you are static_casting an lvalue reference to an object who's most derived type is std::span<T>
, not Span<T>
. You can fix it:
constexpr Span substr(std::size_t pos, std::size_t count){
return subspan(pos, count);
}
However, I wouldn't do this. I would instead change the calling code to use free functions that you've overloaded for std::span<T>
and std::string_view
(et. al.)
template<typename T>
constexpr void remove_prefix(std::span<T> & span, std::size_t n) {
span = span.subspan(n);
}
template<typename CharT, typename Traits>
constexpr void remove_prefix(std::basic_string_view<CharT, Traits> & view, std::size_t n) {
view.remove_prefix(n);
}
template<typename T>
constexpr std::span<T> substr(std::span<T> span, std::size_t n) {
return span.subspan(n);
}
template<typename CharT, typename Traits>
constexpr std::basic_string_view<CharT, Traits> substr(std::basic_string_view<CharT, Traits> view, std::size_t n) {
return view.substr(n);
}
答案2
得分: 1
So, I think you should consider changing your design rather than changing STL.
First, it is unsafe to derive from std::span
, because it does not have a virtual destructor, so there is a chance to leak memory: https://stackoverflow.com/questions/2034916/is-it-okay-to-inherit-implementation-from-stl-containers-rather-than-delegate
Secondly, they are not virtual at all, meaning that inheritance is used incorrectly because, as it seems to me, its purpose is to create interfaces to override, so that details of modules can be hidden from each other.
It's hard to say what I would do without knowing your design, but there are some solutions I'd suggest:
- Use polymorphism or explicit template specialization (with
constexpr if
) to define methods for both classes. - Create an interface for your concrete design (solving your final goal) hiding the details of containing one of these views.
- You can construct
std::span<const char>
fromstd::string_view
and use just one of them.
英文:
So, I think you should consider changing your design rather than changing STL.
First, it is unsafe to derive from std::span
, because it does not have a virtual destructor, so there is a chance to leak a memory: https://stackoverflow.com/questions/2034916/is-it-okay-to-inherit-implementation-from-stl-containers-rather-than-delegate
Secondly, they are not virtual at all meaning that inheritance is used incorrectly, because, as it seems to me, its purpose is to create interfaces to override, so that details of modules can be hidden from each other.
It's hard to say what would I do without knowing your design, but there are some solutions I'd suggest:
- Use polymorphism or explicit template specialization (with
constexpr if
) to define methods for both classes. - Create an interface for your concrete desing (solving your final goal) hiding the detail of containing one of this views.
- You can construct
std::span<const char>
fromstd::string_view
and use just one of them.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论