gpt4 book ai didi

c++ - 设计处理字符串/字符参数的函数模板的最佳实践是什么?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:32:48 26 4
gpt4 key购买 nike

我想写一个简单的字符串 split功能。

函数应该取一个 std::basic_string和一个分隔符(可能是 CharTstd::basic_string ),并将结果放入 ContainerT 中.

我的第一个尝试是

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);
}
}

我最终的目标是扩展这个功能来实现:

  1. 最终结果总是std::basic_string<CharT>放入一些conts .
  2. 第一个参数 str可能是 std::basic_string<CharT> , const CharT*或字符串文字。
  3. 第二个参数delimiter可能是 char , 或 std::basic_string<CharT>/const CharT*/string literal,意思是分隔符的长度大于1,例如拆分 aaa,,bbb,c,,给出 aaa/bbb,c .
  4. 第三个参数可以是来自STL 的任何序列容器。 .

由于一个通常处理 C++ 中的现代字符串,因此 2 可能是 std::basic_string<CharT>仅用于简化。

鉴于函数(模板)可以重载,我想知道

  1. 在这种情况下我至少需要多少个函数?
  2. 设计此类函数的最佳实践是什么(如何编写更通用的函数)?例如,也许要使上述功能与 const CharT* 一起使用分隔符,行 std::size_t len = delimiters.size();必须改成一些std::distance(...)

更新:

添加了一个有效的代码审查 here .

最佳答案

您可以对要拆分的文本和分隔符使用 std::string_view。此外,您可以使用模板模板参数来选择结果中的元素类型:

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); }

这样,它可以与 std::stringstd::string_viewconst char* 一起使用,无需冗余分配:

// vector of std::string_view objects:
auto words_1 = split<std::vector>("hello world", " ");

// list of std::string objects:
auto words_2 = split<std::list, std::string>(std::string("hello world"), " ");

// vector of std::u16string_view objects:
auto words_3 = split<std::vector>(u"hello world", u" ");

编辑:为 charchar16_t 添加重载

编辑2

在上面的代码中,split_impl 执行实际工作。 split 重载仅用于简化用户代码,因此您不必明确指定要使用的字符类型。如果没有重载,这将是必要的,因为当参数类型为 basic_string_view 并且您正在传递不同类型的参数(例如,const char*std::wstring)。总的来说,我认为这不是什么大问题 - 可能您想要四个重载 (charchar16_tchar32_twchar_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", " ");

使用这种方法,您不能像上面那样使用 String 模板参数的默认值(因为它必须依赖于 TextT 类型)。出于这个原因,我删除了它。此外,此代码假定 textdelim 使用相同的字符类型,并且可以转换为 basic_string_view

就个人而言,我更喜欢版本 1。它不使用函数参数的模板类型,恕我直言,这是更好的,因为它让调用者更好地了解应该传递什么。换句话说,第一个 的接口(interface)split 更好地指定。另外,如上所述,我认为必须添加四个 split 重载不是问题。

关于c++ - 设计处理字符串/字符参数的函数模板的最佳实践是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50549688/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com