std::string s1 {"Modern C++", 3} 与 std::string s1 {str, 3} 的区别是什么?

69
以下代码的输出让我感到困惑:
const std::string str = "Modern C++";

std::string s1 {"Modern C++", 3};
std::string s2 {str, 3};

std::cout << "S1: " << s1 << "\n";
std::cout << "S2: " << s2 << "\n";

输出:

> S1: Mod
> S2: ern C++

有人能解释一下这个结果吗?


3
你有读过关于std::string构造函数的参考文献吗?"Modern C++"不是一个std::string,而是一个字符数组(会自动转换为char指针),有一个特定的构造函数适用于字符数组。 - HolyBlackCat
40
好的。参考资料会告诉你这一点。我相信C++标准库委员会有些非常巧妙的理由,可以用无可挑剔的逻辑和推理向我证明为什么它们不执行相同的操作。然而,这只是C++标准库仍然难以理解,甚至令人沮丧的一个小原因。 - Tumbleweed53
2
@HolyBlackCat 不过,结果应该是一样的,无论如何还是谢谢。 - dbenarfa
4
“惊人聪明的原因”是std::string最初设计于几十年前,因此回顾一些其构造函数和函数不符合我们认为的现代直观界面。 - M.M
1
说实话,我认为真正的问题在于每个类只有一个构造函数名称 - 类名。您不能为同一类使用不同名称的不同构造函数,而且只能由于重载而拥有多个构造函数。具有相同名称但执行不同操作的不同重载是令人不舒服的,但这就是我们为构造函数所困扰的。除非您应该从返回所需值的命名函数中移动构造 - 根本不要使用所有令人困惑的构造函数,它们是我认为过时的遗物,属于移动语义之前的时代。 - user180247
显示剩余5条评论
4个回答

66

来自:

https://en.cppreference.com/w/cpp/string/basic_string/basic_string

std::string s1 {"Modern C++", 3};

使用下面的构造函数:

basic_string( const CharT* s,
          size_type count,
          const Allocator& alloc = Allocator() );

因此,需要3个字符才能得到Mod

std::string s2 {str, 3};

将使用以下构造函数:

basic_string( const basic_string& other,
          size_type pos,
          const Allocator& alloc = Allocator() );

因此,从第3个位置开始获取字符串,得到: ern C++.


3
谢谢。第一个是 size_type count,第二个是 size_type pos - dbenarfa
28
必须要问,为什么他们会添加两个行为如此不同的构造函数。就像它是随意拼凑出来的,没有任何考虑。 - Polygnome
4
“Cobbled together with no thought at all”基本上描述了整个std::string。这也解释了基于索引和迭代器的方法混合使用的情况——string是独立于STL开发的,后来进行了修补以匹配STL,并且通常具有太多“便捷方法”。我总是喜欢GOTW #84: Monoliths Unstrung,其中介绍了一个最小的字符串API,并辅之以自由函数。 - Matthieu M.
2
这个子字符串构造函数是否有任何使用情况,而 substr 方法无法满足呢? - NotThatGuy

35

一个是调用string(char const*, count),另一个是string(string const&, pos)

一个从缓冲区中获取前3个字符,另一个获取第3个之后的所有字符。

这是因为C++有原始字符缓冲区和std字符串。"this is not a std::string""this is a std string"sstd::string so_is="this";

std::string已经有30多年的历史了,它被添加到C++语言中时并没有得到足够的关注(不像STL,在被添加之前经历了更多迭代)。

它的接口太丰富了,可能会遇到诸如此类的问题;多个重载导致令人困惑的结果。


7

有人能解释一下这是为什么吗?

这是因为 std::string 有些本不应该有的构造函数(@ORR 解释了细节)。而它不应该有这些构造函数,因为:

  1. 它们的效果可以通过命名构造函数惯用法 / std::string 方法和现有构造函数轻松实现 - 不需要额外的成本(至少在 C++11 中),以及
  2. 仅从构造函数调用就很难理解构造函数参数的使用方式。

这不是标准库中唯一存在这种不良(IMHO)构造函数的情况;std::vector 因其过多的构造函数变体和令人困惑/误导的构造函数语义而闻名。

生活教训:

  • 不要过度使用构造函数;并非每个常用的值都需要拥有自己的构造函数来构建该类对象;
  • 相反,使用命名构造函数惯用法
  • 请让您的代码审阅人员或其他不那么偏见的人阅读构造函数的调用,以判断每个构造函数的含义是否足够明显。

3

如果您想要获得与以下相同的输出结果:

s1上的 Mod

s2上的 Mod

您可以使用字符指针来处理字符串,例如:

char * str = "Modern C++";

std::string s1 {"Modern C++", 3};
std::string s2 {str, 3};

std::cout << "S1: " << s1 << "\n";
std::cout << "S2: " << s2 << "\n";

输出
 Mod

 Mod

为了使其正常工作,第一行应该是: char str[] = "现代 C++"; 或者 char str[] {"现代 C++"}; - dbenarfa
尝试一下,对我来说它完全正常。在C++/C中,您还可以使用指针声明和初始化字符数组。char * str = "Modern C++" ; 现在str是一个数组,因此可以使用str[value]进行循环遍历。 - kelly43
我认为没有 [ ] 的语法是 char ** str。 - dbenarfa
char * str = "Modern C++"; 这行代码在现代的C++中是无效的,具有讽刺意味。从C++11开始,需要使用const。https://wandbox.org/permlink/e3TA4tKbrkBOOUll (gcc) / https://wandbox.org/permlink/TFTRyB565SVyhMlb (clang) - Cubbi

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