连接字符串族

Concatenate a family of string

本文关键字:字符串 连接      更新时间:2023-10-16

我想编写一个函数,将任何std::string序列与幕后只有一个malloc连接起来。因此,需要首先计算字符串的总长度。该函数需要以这种方式使用:

std::string s0 = ...;
std::string s1 = ...;
std::string s2 = ...;
std::string s = join(s0, s1, s2);

更好的join是混合使用std::stringstd::string_view。如果我们可以添加字符串文字,那就更好了。您将如何在 C++11 中编写这样的函数(它需要使用gcc 4.8.5Visual Studio 2015进行编译(?

如果你有一个string_view类型,那么你可以接受一个string_view集合,对它们的大小求和,分配,然后复制。

std::string join(std::initializer_list<string_view> values)
{
std::string result;
result.reserve(std::accumulate(values.begin(), values.end(), 0, [](string_view s) { return s.length(); }));
std::for_each(values.begin(), values.end(), [&result](string_view s) { result.append(s.data()); });
return result;
}

使用 range-v3,您可以使用concat

const std::string s = ranges::view::concat(s0, s1, " worldn");

下面是一个可能的实现:

template<typename... Args>
std::string join(const Args&... args)
{
size_t size = 0;
for( const std::string& s : { args... } )
size += s.size();
std::string result;
result.reserve(size);
for( const std::string& s : { args... } )
result += s;
return result;
}

演示:https://wandbox.org/permlink/qwG0LMewsHwVuGXN

> Benoit 几乎拥有它,他的实现只需要必要的样板来处理不同的字符串类型。 值得称赞的是,我必须承认这是相当多的样板。:)

使用gcc -std=c++17编译

#include <string>
#include <string.h>
#include <string_view>
#include <iostream>
namespace detail {
template<typename C> 
size_t myStrLen(const std::basic_string<C>& s) { return s.length(); }
template<typename C> 
size_t myStrLen(const std::basic_string_view<C>& s) { return s.length(); }
size_t myStrLen(char)    { return 1; }
size_t myStrLen(wchar_t) { return 1; }
size_t myStrLen(const char* s) { return strlen(s); }
size_t myStrLen(const wchar_t* s) { return wcslen(s); }
template<typename T, typename...Args>
size_t myStrLen(T&& t, Args&&...args)
{
return myStrLen(std::forward<T>(t)) + myStrLen(args...);
}
template<typename C, typename T>
void myConcat(std::basic_string<C>& result, T&& t)
{
result += t;
}
template<typename C, typename T, typename...Args>
void myConcat(std::basic_string<C>& result, T&& t, Args&&...args)
{
result += t;
myConcat(result, args...);
}
}
template<typename C, typename... Args>
auto join(Args&&... args)
{
std::basic_string<C> result;
result.reserve(detail::myStrLen(args...));
detail::myConcat(result, args...);
return result;
}
template<typename... Args>
auto join(Args&&... args)
{
return join<char>(args...);
}
template<typename... Args>
auto wjoin(Args&&... args)
{
return join<wchar_t>(args...);
}
int main()
{
std::string str{"hello"};
std::string_view sv{"world"};
std::wstring wstr{L"hello"};
std::wstring_view wsv{L"world"};

std::cout << join(str, " ", sv, 'n');
std::wcout << wjoin(wstr, L" ", wsv, L'n');
return 0;
}

[编辑] 将模板join<charType>移出命名空间详细信息,因为这可能对其他模板构造有用。

我相信使用完美的转发和可变参数模板参数是可能的。

joinStr.hpp :

class joinStr {
public:
template<typename... Args>
joinStr(const std::string& first, Args&&... args): ret(""), size(0) {
size = first.length();
getSize((args)...);
std::cout << "before reserve : " << ret.capacity() << std::endl;
ret.reserve(size);
std::cout << "after reserve : " << ret.capacity() << std::endl;
ret.append(first);
join(std::forward<Args>(args)...);
}
~joinStr() {}
std::string getStr() {
std::cout << ret << std::endl;
return ret;
}
private:
std::string ret;
int size;
int getSize() {
std::cout << "size : " << size << std::endl;
return size;
}
template<typename... Args>
int getSize(const std::string& first, const Args&... args) {
size += first.length();
return getSize((args)...);
}
template<typename... Args>
int getSize(char* first, const Args&... args) {
size += strlen(first);
return getSize((args)...);
}
template<typename... Args>
int getSize(const char* first, const Args&... args) {
size += strlen(first);
return getSize((args)...);
}
void join() {
std::cout << "Final capacity : " << ret.capacity() << std::endl;
}
template<typename... Args>
void join(const std::string& first, Args&&... args) {
ret.append(first);
join(std::forward<Args>(args)...);
}
template<typename... Args>
void join(char* first, Args&&... args) {
ret.append(first);
join(std::forward<Args>(args)...);
}
template<typename... Args>
void join(const char* first, Args&&... args) {
ret.append(first);
join(std::forward<Args>(args)...);
}
};

主要:

int main() {
std::string s1 = "hello";
std::string s2 = "world !";
std::string s3 = "meatpopsicle";
const char* s4 = "__yeah__";
joinStr c(s1, s2, s3, s4);
return 1;
}

请注意,我将其包装在类中,但您可以在类外执行相同的操作。

作为奖励,它适用于 std::string、const char* 和 char*。

编辑:修复了在getSize中重用移动值的问题,并在Caleth的建议后为常量字符*添加了模板

编辑 2: 允许在常量字符*之后传递size_t字段而不是使用 strlen 的版本。

class joinStr {
public:
template<typename... Args>
joinStr(const std::string& first, Args&&... args): ret(""), size(0) {
size = first.length();
getSize(args...);
std::cout << "before reserve : " << ret.capacity() << std::endl;
ret.reserve(size);
std::cout << "after reserve : " << ret.capacity() << std::endl;
ret.append(first);
join(std::forward<Args>(args)...);
}
~joinStr() {}
std::string getStr() {
std::cout << ret << std::endl;
return ret;
}
private:
std::string ret;
int size;
int getSize() {
std::cout << "size : " << size << std::endl;
return size;
}
template<typename... Args>
int getSize(const std::string& first, const Args&... args) {
size += first.length();
return getSize((args)...);
}
// const char *
template<typename... Args, typename T>
int getSize(const char* first, const T& t, const Args&... args) {
getSizeImpl<T>(first, t, std::integral_constant<bool, std::is_same<T, size_t>::value>());
return getSize(t, args...);
}
template<typename T>
void getSizeImpl(const char* first, const T& t, std::false_type) {
// Case when the next argument is not a size_t type
size += strlen(first);
}
template<typename T>
void getSizeImpl(const char* first,const T& t, std::true_type) {
// Do nothing
}
template<typename... Args>
int getSize(size_t s, const Args&... args) {
size += s;
std::cout << "size_t " << s << " in getSize" << std::endl;
return getSize(args...);
}
void join() {
std::cout << "Final capacity : " << ret.capacity() << std::endl;
}
template<typename... Args>
void join(const std::string& first, Args&&... args) {
ret.append(first);
join(std::forward<Args>(args)...);
}
template<typename... Args>
void join(char* first, Args&&... args) {
ret.append(first);
join(std::forward<Args>(args)...);
}
template<typename... Args>
void join(const char* first, Args&&... args) {
ret.append(first);
join(std::forward<Args>(args)...);
}
template<typename... Args>
void join(size_t s, Args&&... args) {
join(std::forward<Args>(args)...);
}
};

主要:

int main() {
std::string s1 = "hello";
std::string s2 = "world !";
std::string s3 = "meatpopsicle";
const char* s4 = "__yeah__";
joinStr c(s1, s2, s3, s4, (size_t)8);
return 1;
}