C++ 设计函数模板来处理字符串/字符参数的最佳实践是什么?
我想写一个简单的字符串C++ 设计函数模板来处理字符串/字符参数的最佳实践是什么?,c++,c++17,C++,C++17,我想写一个简单的字符串split函数 该函数应使用一个std::basic_字符串和一个分隔符(可能是图表或std::basic_字符串),并将结果放入容器中 我的第一次尝试是 template <typename StringT, typename DelimiterT, typename ContainerT> void split( const StringT &str, const DelimiterT &delimiters, ContainerT
split
函数
该函数应使用一个std::basic_字符串
和一个分隔符(可能是图表
或std::basic_字符串
),并将结果放入容器中
我的第一次尝试是
template <typename StringT, typename DelimiterT, typename ContainerT>
void split(
const StringT &str, const DelimiterT &delimiters, ContainerT &conts) {
conts.clear();
std::size_t start = 0, end;
std::size_t len = delimiters.size();
while ((end = str.find(delimiters, start)) != StringT::npos) {
if (end - start) {
conts.emplace_back(str, start, end - start);
}
start = end + len;
}
if (start != StringT::npos && start < str.size()) {
conts.emplace_back(str, start, str.size() - start);
}
}
模板
空分(
常量StringT&str、常量分隔符rt&delimiters、ContainerT&conts){
继续清除();
标准::大小\u t开始=0,结束;
std::size\u t len=分隔符.size();
while((end=str.find(delimiters,start))!=StringT::npos){
如果(结束-开始){
续:后置(str、start、end-start);
}
开始=结束+长度;
}
if(start!=StringT::npos&&start
我的最终目标是扩展此功能以实现:
最终结果总是std::basic_string
放入一些conts
第一个参数str
可以是std::basic_string
、const CharT*
或字符串文字
第二个参数分隔符
可以是字符
,也可以是std::basic_string
/const CharT*
/string literal,这意味着分隔符的长度大于1,例如拆分aaa,,bbb,c
和,
给出aaa/bbb,c
第三个参数可以是STL
中的任何序列容器
因为通常在C++中处理现代STOP,5月2日是<代码> STD::Basic字符串只用于简化。
考虑到函数(模板)可以重载,我想知道
在这种情况下,我至少需要多少函数李>
设计此类函数的最佳实践是什么(如何编写更通用的函数)?例如,要使上述函数与const-CharT*
分隔符一起工作,行std::size\u t len=delimiters.size()代码>必须更改为某些标准::距离(…)
更新:
添加了一个可撤销代码审查。从输入字符串构造基本字符串视图
,然后对这些字符串进行操作。basic\u string\u视图
有一个采用char*
的显式构造函数,basic\u string
有一个对basic\u string\u视图
的强制转换操作符,我的建议是使用两个模板参数,一个用于输入字符串,一个用于输出容器,因为在几乎所有情况下,输入字符串,除味器和输出容器将是相同的类型,所以您可以像这样定义您的函数-
template<typename charT, typename Container)
void split(const std::basic_string<charT> input,
const charT deliminator,
Container<std::basic_string<chart>> &cont)
template您可以使用std::string\u视图
对要拆分的文本和delimeter使用。此外,还可以使用“模板”参数选择结果中的图元类型:
template<typename Char, template<typename> class Container, typename String>
Container<String> split_impl(std::basic_string_view<Char> text, std::basic_string_view<Char> delim)
{
Container<String> result;
//...
result.push_back(String(text.substr(start, count)));
//...
return result;
}
template<template<typename> class Container, typename String = std::string_view>
Container<String> split(std::string_view text, std::string_view delim)
{ return split_impl<char, Container, String>(text, delim); }
template<template<typename> class Container, typename String = std::u16string_view>
Container<String> split(std::u16string_view text, std::u16string_view delim)
{ return split_impl<char16_t, Container, String>(text, delim); }
编辑:为char
和char16\t
编辑2
在上面的代码中,split\u impl
执行实际工作<提供代码>拆分
重载只是为了简化用户代码,因此您不必显式指定要使用的字符类型。如果没有重载,这是必要的,因为当参数的类型为basic\u string\u view
并且您正在传递不同类型的参数(例如,const Char*
或std::wstring
)时,编译器无法推断Char
。一般来说,我认为这不是一个大问题-可能,您希望有四个重载(char
,char16\u t
,char32\u t
,wchar\u t
),如果不是更少的话
但是,为了完整起见,这里有一个不使用重载的替代方案:
template<typename ContainerT, typename TextT, typename DelimT>
ContainerT split(const TextT& text, const DelimT& delim)
{
using CharT = std::remove_reference_t<decltype(text[0])>;
std::basic_string_view<CharT> textView(text);
std::basic_string_view<CharT> delimView(delim);
ContainerT result;
// actual implementation, but using textView and delimView instead of text and delim
result.push_back(textView.substr(start, count));
return result;
}
// usage:
auto words = split<std::vector<std::string_view>>("some text", " ");
模板
ContainerT拆分(常量文本和文本、常量删除和删除)
{
使用CharT=std::删除\u参考\u t;
标准::基本字符串视图文本视图(文本);
std::基本字符串视图delimView(delim);
ContainerT结果;
//实际实现,但使用textView和delimView而不是text和delim
结果。推回(textView.substr(开始,计数));
返回结果;
}
//用法:
自动单词=拆分(“某些文本”,“拆分”);
使用这种方法,您不能像上面那样使用String
template参数的默认值(因为它必须依赖于TextT
type)。出于这个原因,我将其删除。此外,此代码假定text
和delim
使用相同的字符类型,并且可以转换为基本字符串视图
就个人而言,我更喜欢版本1。它不使用模板类型作为函数参数,这是更好的,因为它让调用者更好地了解应该传递什么。换句话说,最好指定第一个拆分的接口。此外,正如上面提到的,我不认为必须添加四个重载的<代码>分割<代码>一个问题。我发现这是棘手的,因为所有潜在的组合。我尝试过很多方法。我当前使用的一个主模板函数只对输入字符串和分隔符使用迭代器(四个迭代器s_begin、s_end、d_begin、d_end)。然后我有很多不同的重载,它们将输入参数转换成最基本的形式。您看到了关于是否应该使用一些std::basic_string_view
作为模板函数参数的顶部答案了吗?如果传递一个std::string
它似乎不能被隐式转换,这在传递参数时似乎失去了一些便利。std::string
有一个转换操作符用于转换到std::string\u视图
,所以这应该不是问题。谢谢您的回答。此版本似乎不适合wstring/u16string/u32string
。虽然string
可以隐式转换为string\u视图
,basic\u string
似乎无法转换为basic\u string_
template<typename ContainerT, typename TextT, typename DelimT>
ContainerT split(const TextT& text, const DelimT& delim)
{
using CharT = std::remove_reference_t<decltype(text[0])>;
std::basic_string_view<CharT> textView(text);
std::basic_string_view<CharT> delimView(delim);
ContainerT result;
// actual implementation, but using textView and delimView instead of text and delim
result.push_back(textView.substr(start, count));
return result;
}
// usage:
auto words = split<std::vector<std::string_view>>("some text", " ");