当T不是字符类型时,basic_string<T>有什么用处吗?

21

C++字符串的声明如下

template< 
    class CharT, 
    class Traits = std::char_traits<CharT>, 
    class Allocator = std::allocator<CharT>
> class basic_string;

CharT 是字符类型,可以是 charwchar_tchar16_tchar32_t;但是,basic_string 是一个模板,因此可以使用其他的 CharT 和其他分配器进行实例化。虽然我可以想到一些使用其他分配器的用例,但我无法想象其他数据类型的字符串的用例,例如:

using string = std::basic_string<int>;

使用整数字符串,我们不能将其初始化为字符串(显而易见),也不能将其初始化为u32字符串(至少对我来说不是很明显);但只要列表中包含的类型可转换为int,我们就可以使用initializer_list进行初始化:

string err1("test");   // Error!
string err2(U"test");  // Error!
string err3{"test"};   // Error!
string err4{U"test"};  // Error!
string err5 = "test";  // Error!
string err6 = U"test"; // Error!

string success1({U't', U'e', U's', U't'});
string success2 = {U't', U'e', U's', U't'};
string success3({'t', 'e', 's', 't'});
string success4 = {'t', 'e', 's', 't'};

即使我们成功将一个整数字符串初始化,也不能以常规方式使用它:

std::cout << success1; // Error! expected 116101115116

唯一可以与cout一起使用的basic_string是普通的那些,这很合理:毕竟我们不能假设一个整数字符串或MyFancyClass字符串应该如何打印。
但不管怎样,创建奇怪的basic_string实例并没有被禁止;一方面,由于缺乏禁止这种用法的功能(即概念),另一方面,编写不限制底层类型的basic_string比相反的方式更容易(没有概念),所以,这让我想知道:
  • 是否有任何使用std::basic_string<T>,其中T不是字符类型的情况?
至于任何用途,我在考虑只能通过T字符串实现且无法使用T向量(或者使用T向量将会更加困难)的事情,换句话说:
  • 您是否曾经遇到过需要使用T字符串的情况?

1
@BoBTFish,通过添加输出运算符不是很容易解决吗?OP正在寻求只能使用T字符串实现而无法使用T向量完成的事情(或者这将变得非常困难) - NathanOliver
1
@tobi303 我认为你是对的,但是方向相反了:“使用std::basic_string<int>有什么不能用std::vector<int>实现的功能呢?” - PaperBirdMaster
7
在 C++11 之前,我们使用 std::basic_string<_uint32>(并使用为其专门化的 std::char_traits)来存储 UTF32 字符串。在某些地方,我们使用其他字符 traits 来实现诸如忽略大小写比较或通配符搜索之类的操作。 - Revolver_Ocelot
2
就C++98而言,basic_string保证是连续的,而vector则不是。 - edmz
1
@black 我不认为这是正确的。basic_string的连续性保证只在C++11中添加了。 - Barry
显示剩余12条评论
2个回答

3

如果你正在处理一个处理多种文本编码(例如ASCII和EBCDIC)的系统,你可能希望为每种编码使用单独的字符类型和字符串类型。它们都应该被视为字符串,但是明显是不同的类型。


这对我来说似乎不太合理,因为你几乎需要在所有使用文本的函数上使用模板,从而增加编译时间和二进制文件大小。我宁愿使用通用的二进制字符字符串,并构建开关或仅在内部使用Unicode编码,以避免处理区别。 - Jimmy T.

2
构建分词器时,char[n] 或 enum 是常见的选择。需要注意的是,Token 的构造函数不会被 basic_string 调用。
#include <iostream>
#include <string>
#include <string.h>

static const int max_token_length = 10;

struct Token
{
    char str[max_token_length];
};

// define std::char_traits<Token> if your implementation defaults are not fine for you (or don't exist as it's not required)

int main() {
    Token t;
    strncpy( t.str, "for", max_token_length );
    std::basic_string<Token> s1( 1, t );

    Token u;
    strncpy( t.str, "for", max_token_length );
    std::basic_string<Token> s2( 1, u );

    std::basic_string<Token> s = s1 + s2;

    return 0;
}

2
“构造函数未运行”是什么意思?当然已经运行了!就在这一行代码中:std::basic_string<Token> s;,这是默认构造函数。顺便说一下,根据我的记忆和这个网站(http://en.cppreference.com/w/cpp/string/basic_string/basic_string),basic_string的默认构造函数将其初始化为空实例(零长度字符串,“未指定容量”)。这意味着s[0]可能还不存在,直到您正确设置它。 - quetzalcoatl
1
@quetzalcoatl:Token的构造函数没有运行(只执行了赋值操作),basic_string<Token>的构造函数当然会被执行。编辑以澄清这一点。你可以验证一下:如果在构造函数之前添加一个无效的赋值类型,通常会失败。这就是(其中之一)为什么我们不能有std::basic_string<std::basic_string<char>>的原因之一。 - lorro
1
关于整个答案,您不需要定义std::char_traits<Token>的特化吗?包括一个eof值和其他内容。 - KABoissonneault
1
让我们不去理会 strcpy 非常危险而且永远都不应该使用(我们有 strncpy,它仍然很危险但是较少)。让我们不去理会你没有为你的类型专门化 char_traitss[0] 是必须存在的,但它代表着一个值初始化的 charT 值(普通字符串的空终止符) 。你不被允许写终止符值。所以这是你的代码错误的另一种方式。 - Nicol Bolas
1
s[0] 总是存在的。在 http://www.cplusplus.com/reference/string/string/string/ 中:'(1) 空字符串构造函数(默认构造函数)构造一个长度为零的空字符串。' 请注意,容量不一定等于长度;两者都是 size_t 类型。operator 定义为对于大小,它返回对 '\0' 的引用;在 C++11 中,这个位置位于最后一个字符之后。 - lorro
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接