【C++11】从std::string str = “foo”说开去

最近因为某些原因决定重新开始学习C++。考虑到自己在大学里面学到的C++有点旧(估计是C++98),所以打算从C++11开始。

C++11如其名,是2011年出来的标准,所以2011年之后才有编译器实现。现在2019年大部分PC以及服务器应该都支持C++11了。

个人习惯于看书来学习某样东西,所以找了C++相关书的资料。一开始在o’reilly上找,发现很多书都比较旧。虽然有Effective C++以及More Effective C++系列,但对于初学者来说还不是时候。最后在Stackoverflow上找到了一个比较全的推荐书列表

https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list

比如面向初学者

  • 《C++ Primer》(The fifth edition (released August 16, 2012) covers C++11)
  • 《Programming: Principles and Practice Using C++》 (Bjarne Stroustrup, 2nd Edition – May 25, 2014)

面向有编程经验的人

  • 《A Tour of C++》
  • 《Accelerated C++》

个人最后选择了可能是最全的C++作者写的《The C++ Programming Language》。原因也很简单,希望全面地,与时俱进地学习C++11。

到现在,看了100多页(总共有1300多页),不得不说,C++11和大学学的有很大不同,以及C++和C最好还是分开来看,C的编程理念和C++还是有所不同的。

具体到实际代码,我想以

为例子展开一下。

首先,右边的string literal,类型是const char*,和左边的std::string不同。那么string literal被转换成了string么?

直观上可以怎么理解,但背后不是这样。这里“转换”的前提是string有一个参数是const char*的构造函数。

考虑自己写一个StringLiteral类,设计一个以const char*类型为参数的构造函数。

执行上述程序,你可以发现输出了以下内容

也就是说,构造函数被调用了。这不是碰巧试出来的特性,而是一个叫做converting constructor的东西。简单来说,如果你构造函数上没有加explicit的话,就可以作为converting constructor。换句话说,如果上述程序中,构造函数前加了explicit(注释don’t add explicit here),那么程序就无法编译。加了explicit的话,StringLiteral必须以类似下面这种显示的方法构造

回到没有explicit的程序的输出。从程序输出来看,似乎仅仅是StringLiteral的构造函数被调用了,又考虑到StringLiteral不需要被move,那么是否可以把StringLiteral的move constructor给delete掉?

答案是不可以(这里个人觉得是C++一个很不直观,或者说坑人的地方,虽然move构造函数没有被调用但是不允许删除)。move函数不能被删除的理由,个人理解是,可能部分编译器不支持elide-constructor(构造函数消除)优化。

同样是上述程序,在编译的时候,加入 -fno-elide-constructors 标志,即取消构造函数消除优化

再次执行时,你可以看到以下输出

似乎和之前的结果不一样?实际编译器做的事情类似

如果你高兴,你还可以注释掉move constructor,看下结果

从move constructor变成了copy constructor。实际代码类似

这里到底发生了什么?

答案在这里

再看一次

在C++里这其实触发了copy-initialization。具体来说内部使用converting constructor构造了一个临时string变量,然后使用copy constructor再次构造了str。注意这里并不会调用copy assignment构造函数(上面的程序中copy/move assignment都delete掉了,间接证明了不会被调用)。

似乎没有move constructor的事情?介绍copy initialization的页面中有一句

也就是说,如果有用户定义了的move constructor的话,会优先使用move constructor。

至此,为什么输出会有copy/move构造函数都可以理解了。

当然,这里明显有一次多余的构造,所以编译器会执行构造消除优化。

小结

老实说C++很难,从上面这一个语句背后需要理解的概念就说明完全理解C++,或者说避免C++的坑需要学习很多东西。不管怎么说,一点一点积累,把基础打扎实最重要。