#include "某 C++ 概念之序篇"
答:
std::stringstream()
产生的临时变量无法匹配类型
std::stringstream&
.
但是
std::stringstream()
可以匹配类型
std::stringstream const&
, 也就是说, 如果函数
std::stringstream& operator<<(std::stringstream& os, double d)
的签名和实现作修改成下面这个样子就行了
std::stringstream const& operator<<(std::stringstream const& os, double d)
{
const_cast<std::stringstream&>(os).operator<<(d);
return os;
}
之前扯过
C++ 已经没有语法意义上的左值了, 不过这里似乎有一点点很微妙的, 跟左值很类似的东西, 那就是, C++ 引用. 任何一本 C++ 入门书籍里面都会花一些篇幅来讨论引用, 其基本意义, 无非是
某个东西的别名, 因此, 这
某个东西对于引用而言显然是至关重要的.
最基本的一点是, 如果被引用的东西是临时变量 (顺便一提, 如果是字面常量这种比较尴尬的情况, 视同临时变量) 怎么办? 比如之前代码样例 A 中的情况?
这就牵扯到 C++ 中正统的
left value. 因为 left 已经不再有任何 "左" 相关的意义的, 所以我不太想在本文中把这货称之为 "左值" (实际上英文在这些方面更加灵活, 比如 Copyleft 这词跟 left 也没太大关系, 用来吐槽则非常不错).
一个东西如果是 left value, 那么它就可以被赋值给 left value 引用, 所谓 left value 引用嘛, 就是就是类似
std::stringstream&
的引用类型, 而暗含
const
的类型的引用则不算
typedef int const int_const;
int_const& this_is_not_a_left_value_reference;
那究竟什么是 left value 呢?
一个语句之后仍然存在的变量, 表达式, 或者这样类似的 "具有
值属性的什么东西" 就是 left value. 而
std::cout << (std::stringstream() << d).str().length() << std::endl;
这句之后, 显然临时构造的
std::stringstream()
就被析构了, 因此这家伙不算是 left value, 自然也就无法被
std::stringstream&
所引用.
那常数为什么不算 left value 呢? 当然, 可以认为常数居阴阳之外, 别谈语句之后, 吐核退出, 就算是宇宙毁灭, 什么 π 啊 e 啊还活得好好的; 不过, 从计算机的角度来理解, 常数是 CPU 指令的立即数, 所以别谈语句之后, 只要这条指令结束, 它就没了.
然而, C++ 一个很邪恶的规定是, 即使一个东西不是 left value, 它仍然可以绑定
const
引用, 比如以下语句都是合法的
std::pair<int, int> const& pair = std::make_pair(0, 1);
std::stringstream const& strm = std::stringstream();
std::string const& x = "Raki suta";
int const& i = 0;
这样直白地写出来是有点让人觉得不舒服, 不过作为函数调用的参数则好得多, 比如之前的代码片段 B 那样