如何定义泛化to_string和to_wstring以避免代码重复

问题描述 投票:0回答:1

我有许多用户定义的类,我想为其定义一个 to_string 函数。然而,我不想只为

std::string
定义它,而是为
std::basic_string
的所有可能实例定义它。因此,这是一个天真的尝试,但失败了:

我们有一些类型,并且我们为每个类型定义了

to_basic_string()
函数。然后我们有更具体的
to_string()
to_wstring()
to_u8string()
,它们围绕每个
to_basic_string()
实例化的
basic_string
。使用通用参数,因此我们不必为每个用户定义的类型定义相同的东西。

#include <string>
#include <iostream>
#include <sstream>
using namespace std::literals;

// Define the types...

template <std::size_t... Is>
struct foo {};

struct bar {};

struct baz {};

// ...


// Define the generic to_basic_string functions...

template<typename char_type, std::size_t first, std::size_t... Is>
std::basic_string<char_type> to_basic_string(foo<first, Is...> arg)
{
    std::basic_stringstream<char_type> stst;

    stst << "foo<"s << first;

    if (sizeof...(Is) > 0)
    {
        ((stst << ","s << Is), ...);
    }

    stst << ">"s;

    return stst.str();
}

template<typename char_type>
std::basic_string<char_type> to_basic_string(bar arg)
{
    using namespace std::literals;

    return "bar"s;
}

template<typename char_type>
std::basic_string<char_type> to_basic_string(baz arg)
{
    using namespace std::literals;

    return std::basic_string<char_type>{};
}

// ...


// Define (for generic argument) functions that convert to the particular basic_string instantiations, such as string, wstring, etc.

std::string to_string(auto arg)
{
    return to_basic_string<char>(arg);
}

std::wstring to_wstring(auto arg)
{
    return to_basic_string<wchar_t>(arg);
}

std::u8string to_u8string(auto arg)
{
    return to_basic_string<char8_t>(arg);
}

// ...

int main()
{
    std::cout << to_string(foo<0, 2, 4, 6, 8>{}) << "\n"s;
    std::cout << to_string(bar{}) << "\n"s;
    std::cout << to_string(baz{}) << "\n"s;

//  std::wcout << to_wstring(foo<0, 2, 4, 6, 8>{}) << L"\n"s;
//  std::wcout << to_wstring(bar{}) << L"\n"s;
//  std::wcout << to_wstring(baz{}) << L"\n"s;
}

将其用于

to_string()
时,一切似乎工作正常。对于
to_wstring()
和其他函数,它无法编译,这并不奇怪:
to_basic_string()
函数都使用
basic_string
文字,例如
"foo<"s
,仅适用于基于
char
的文字。

这就提出了一个问题:如何让这个函数支持各种

basic_string
类型,而不必有效地复制函数逻辑?请注意,由于示例较少,所提供的函数具有相当简单的实现,因此并不反映其实际的对应函数。但是,它们确实反映了涉及的一般元素:始终使用字符串文字,并且使用字符串流来连接元素。

至于 c++ 标准的选择,对于这个问题来说,任何达到 c++20 的标准都是公平的。

(顺便说一句,考虑到标准中对它们的支持是多么稀疏,我质疑尝试提供

basic_string
string
之外的支持是否有很大用处。有
wstring
std::cout
,但是据我所知,
std::wcout
u8string
u16string
没有这样的等价物。)
    

c++ tostring string-literals stdstring widestring
1个回答
0
投票

这些模板函数将从一些特定函数中调用,每种 basic_string 调用一个函数(但对于所有类都是通用的,使用

u32string

参数)。

实际上,使用 

auto

参数的函数是“缩写函数模板”,最终您将得到两层嵌套的模板函数。有帮助吗?

无论如何,您面临两个(相关的)问题。

    您的核心
  1. auto

    模板函数应该返回任何类型的 basic_string,但它们实际上被设计为始终返回“经典”基于字符的字符串(即

    to_basic_string()
    的实例)。
    
    

  2. 更重要。没有预定义的方法可以在基于“basic_string”的所有不同类之间进行转换(或者在基于“经典”字符的
  3. std::basic_string<char>

    string
    wstring
    等)之间进行转换。
    
    

  4. 这是因为像
u8string

char
wchar_t
这样的类型指定了每个字符/实体的
size
,但它们没有提及任何关于 encoding 的内容,这主要取决于实现(并且与“区域设置”等)。事实上,源代码中 char/wchar 文字的编码也不是由标准定义的。 单个

char8_t

字符串通常以某种 ISO-8859 变体(每个字符/字节一个字符)进行编码(或者,也许以 UTF-8 编码,每个字符/实体使用一个或多个字符/字节)。宽

char
wstring 可以使用 UCS-2(一种旧的 UTF-16 限制形式)进行编码。或者不是。
为了解决第一个问题,我会将内部级别模板函数替换为简单函数(除了部分 foo 之外)并执行以下操作:

wchar_t

正如我所说,第二个问题更复杂,您可能无法找到真正通用的解决方案。这里提供的只是一个示例。

最后一点,我将移动内部函数,例如特定类中的

#include <string> #include <iostream> #include <sstream> using namespace std::literals; // Define the types... template <std::size_t... Is> struct foo {}; struct bar {}; struct baz {}; // Define the to_string functions for each class template<typename std::size_t first, std::size_t... Is> std::string to_string(foo<first, Is...> arg) { std::stringstream stst; stst << "foo<"s << first; if (sizeof...(Is) > 0) { ((stst << ","s << Is), ...); } stst << ">"s; return stst.str(); } std::string to_string(bar arg) { using namespace std::literals; return "bar"s; } std::string to_string(baz arg) { using namespace std::literals; return std::string{}; } // Define (for generic argument) functions that convert to the particular basic_string instantiations, such as string, wstring, etc. #include <codecvt> std::wstring to_wstring(auto arg) { // This should work if the (char) string is UTF-8 encoded std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; return converter.from_bytes(to_string(arg)); } std::u8string to_u8string(auto arg) { return to_string(arg); } int main() { std::cout << to_string(foo<0, 2, 4, 6, 8>{}) << "\n"s; std::cout << to_string(bar{}) << "\n"s; std::cout << to_string(baz{}) << "\n"s; std::wcout << to_wstring(foo<0, 2, 4, 6, 8>{}) << L"\n"s; std::wcout << to_wstring(bar{}) << L"\n"s; std::wcout << to_wstring(baz{}) << L"\n"s; }

,删除参数。

    

© www.soinside.com 2019 - 2024. All rights reserved.