这里实际上有两个相关的问题。首先,三元运算符的两个分支需要具有相同的类型(即运算符结果的类型),所以如果Christmas 返回一个string,则不能有另一个三元运算符分支返回ostream&。
第二个问题正是Christmas 据称返回了string。 (我假设 return 语句实际上在 Christmas 函数内。)该函数的目的不是返回 string,而是返回一个字符串,然后递归地返回其他字符串到 cout。所以唯一有意义的返回类型是ostream& 或void。但是如果你进行这样的更改,你会发现cout << Christmas(...) 是一个错误,因为你不能向自己发送(<<)流。
看看如何用ostream& 返回来解决这个问题很有趣,尽管它最终看起来与您当前的程序完全不同。下面的内容本质上是有缺陷的,因为像Christmas 这样的函数应该能够输出到任何流,但我们可以稍后修复它。让我们首先假设该函数返回一个ostream&,并且它也输出到相同的ostream。由于函数返回ostream&,我们应该使用它而不是cout,所以我们可能会有大致这样的东西:
std::ostream& Christmas(int day, int count, bool startStanza) {
return (/* We're not finished */)
? Christmas(/* the next line */) << /* This line */;
: std::cout; /* FIXME */
}
但是,递归现在已经发生了翻天覆地的变化。递归调用发生在之前,我们设法将当前行发送到返回的ostream。所以我们实际上需要从结尾开始,然后递归到开头。事实证明,这并不难,我们甚至可以去掉布尔值:
std::ostream& Christmas(int day, int count) {
return day ? count <= day ? Christmas(day, count + 1) << gift[count] << '\n'
: Christmas(day - 1, 1) << "On the "
<< day
<< " day of Christmas, "
"my true love gave to me:\n"
: std::cout; /* FIXME */
}
如果我们要使用<<,我们不妨保持一致。假设我们想通过以下方式开始整个事情:
std::cout << Christmas;
为此,我们需要两件事:
一个类对象,用来保存ostream&,它的成员函数和上面类似。
I/O manipulator,是一个函数,用来构造对象,调用其成员函数输出颂歌。
这就是它的外观:
class Singer {
friend std::ostream& Christmas(std::ostream& out);
Singer(std::ostream& out) : out(out) {}
std::ostream& sing(int day, int count) {
return day ? count <= day ? sing(day, count + 1) << (count == 1 && day > 1 ? "and " : "")
<< gift[count - 1] << '\n'
: sing(day - 1, 1) << "\nOn the "
<< day
<< "th day of Christmas, "
"my true love gave to me:\n"
: out;
}
private:
std::ostream& out;
static const char *gift[12];
};
std::ostream& Christmas(std::ostream& out) { return Singer(out).sing(12,1); }
现场观看:http://coliru.stacked-crooked.com/a/1f801eadf8f5261e