在新的 C++ 库中处理多种字符串类型

5 c++ string libraries c++11

C++ 拥有的一件事是多个字符串,或者更确切地说是字符类型:char, wchar_t, char16_t, char32_t. 因此,我们有不同的字符串类型定义:std::stringstd::wstringstd::u16stringstd::u32string,这是不同的字符串类型。

而且还不止于此,如果我们谈论的是 Windows 和 COM,还有平台类型,例如BSTRs。我们甚至还没有开始谈论字符编码。

如果您正在构建一个新库,其中一项要求是支持所有这些字符串类型或字符类型,您会怎么做?让我们暂时忘记字符编码。

我正在考虑这个问题,并提出了一些选择,但没有一个是理想的。假设您有一个registry_key类,它必须支持所有这些字符类型,并且其 OM 的一部分或多或少(此处仅说明了其中的一部分):

class registry_key
{
public:
  registry_key(unspecified_string_type keyname);
  unspecified_string_type name() const; 
  unspecified_string_type path() const; 
} 
Run Code Online (Sandbox Code Playgroud)

你会像这样使用它:

registry_key key("HKLM\\Software\\Adobe");
std::string name = key.name();
Run Code Online (Sandbox Code Playgroud)

但是,它必须支持其他字符串类型。此外,没有要求registry_key就字符类型而言,整体必须保持一致,或者对单个字符类型进行操作。您可以调用构造函数并传递 aconst char*但将键的名称作为 a 获取u16string。这是底层平台的反映,它允许你在同一个api集合内调用宽( XxxW)和窄( XxxA) api。这种行为是需要的。

对于构造函数(或带参数的事物),这是微不足道的,因为可以推导出类型。但对于返回字符串但不接受任何输入的函数,它不能。

就选项而言,我有:

1)使用字符类型模板整个注册表项,方法basic_string与stl 中的其他类型相同。所以你会

wregistry_key key(L"HKLM\\Software\\Adobe");
std::wstring name = key.name();

u8registry_key key(u8"HKLM\\Software\\Adobe");
std::u16string name = key.name();
Run Code Online (Sandbox Code Playgroud)

问题是这并没有真正扩展,如果它必须应用于很多类型,任何处理字符串的类型,那就太可怕了。在某种程度上,这是一个糟糕的设计选择,因为有些类甚至与字符串无关,所以为什么首先将其作为模板参数传递。

2)采用和使用单个字符串类型,如u16string, 或u32string。但正如所说,这违背了目标。

3)在函数名称前加上字符类型:

registry_key key("HKLM\\Software\\Adobe");
std::string name = key.name();
std::wstring name = key.wname();
std::u16string name = key.u8name();
std::u32string name = key.uname();
Run Code Online (Sandbox Code Playgroud)

这更好,但仍然是多余的。

4)创建一个新的字符串类型,它根本不是字符串类型。在某种程度上,它是一种变体,可以存储不同类型的字符串,并使用用户定义的转换运算符查询并转换为不同的其他类型的字符串。所以这将是自动的。

platform_string str = L"foo";
std::string sstr = str;
std::wstring swstr = str;
std::u16string su16str = str;
str = u"foo";
Run Code Online (Sandbox Code Playgroud)

这将允许编写一个注册表类,它可能如下所示:

class registry_key
{
public:
  registry_key(unspecified_string_type keyname);
  platform_string name() const; 
  platform_string path() const; 
} 
Run Code Online (Sandbox Code Playgroud)

你可以将它用作:

registry_key key("HKLM\\Software\\Adobe");
std::string name = key.name();
std::wstring name = key.name();
std::u16string name = key.name();
Run Code Online (Sandbox Code Playgroud)

这样做的问题是引入看起来像新字符串类型的东西的想法,即使它不是真的。而且感觉破了。

有比3)4)更好的解决方案吗?或者有更好的方法来解决这个问题?

use*_*003 3

解决此类问题的惯用方法是库设计者选择一种类型的字符串并在整个界面中一致地使用它。如果需要 C 兼容性,请使用 C 格式字符串,否则使用 C++ 字符串。选择库功能所需的字符大小。

让库的调用者处理字符串转换。

否则,你就会陷入混乱。