我正在考虑一个与完美转发有一些相似之处的问题,但是函数参数没有传递给被调用的函数,而是返回了。这就是为什么我称之为“完美传递”。
问题如下:
假设我们有一个函数,它通过引用(可能还有一些额外的参数)获取一个对象,修改该对象,然后返回修改后的对象。此类函数最著名的示例可能是operator<<
and operator>>
对于 iostream。
让我们使用 iostreams 作为例子,因为它可以很好地展示我所追求的内容。例如,人们有时想做的一件事是:
std::string s = (std::ostringstream() << foo << bar << baz).str();
当然这是行不通的,原因有二:
std::ostringstream()
是一个右值,但是operator<<
将左值作为第一个参数
operator<<
返回一个ostream&
(好吧,至少对于标准的实际上是basic_ostream<CharT, Traits>&
where CharT
and Traits
从第一个参数推导出来)。
因此,假设我们要设计插入运算符,以便上述工作正常进行(显然您不能对现有运算符执行此操作,但可以对您自己的类执行此操作)。显然,该解决方案应具有以下特征:
虽然在这个特定的用例中不需要它,但我想添加第三个要求:
- 如果第一个参数是右值,则返回值也是右值,否则返回左值。
这个额外的规则是为了让你可以从通过函数传递的右值进行移动构造(我不知道 C++11 流是否可移动构造,但它旨在成为一个更通用的方案,流就像方便的例子)。
显然,在C++03中,这些要求并不能全部满足。然而,在 C++11 中,我们有右值引用,这应该使这成为可能。
这是我的尝试:
#include <iostream>
#include <sstream>
#include <string>
template<typename Ostream> struct is_ostream
{
typedef typename std::remove_reference<Ostream>::type candidate;
typedef typename candidate::char_type char_type;
typedef typename candidate::traits_type traits_type;
typedef std::basic_ostream<char_type, traits_type> basic_ostream;
static const bool value = std::is_base_of<basic_ostream, candidate>::value;
};
class SomeType {};
template<typename Ostream>
typename std::enable_if<is_ostream<Ostream>::value, Ostream&&>::type
operator<<(Ostream&& is, SomeType const& x)
{
is << "SomeType";
return std::forward<Ostream>(is);
}
int main()
{
SomeType t;
std::string s = (std::ostringstream() << t).str();
std::cout << s << std::endl;
}
它确实是用 gcc 编译的(使用选项-std=c++0x
),运行时输出SomeType
正如预期的那样。然而,到达那里是一个相当复杂的机制。
因此我的问题是:
我编写的输出运算符是否确实按预期工作(并满足我给出的所有要求)?或者它会以意想不到的方式失败或产生其他意想不到的后果吗?特别是:我使用的是std::forward
这里正确吗?
有没有更简单的方法来满足要求?
假设这确实是正确的并且是最简单的方法:您认为这样做是个好主意吗?还是您会建议反对它(既专门针对我的示例中的流输出,又作为传递对象的更通用方案)通过函数)?