我正在考虑一个与完美转发有一些相似性的问题,但是函数参数没有传递给被调用函数,而是返回。这就是我称之为“完美传递”的原因。
问题如下:
假设我们有一个通过引用获取对象的函数(可能还有一些额外的参数),修改该对象,并返回修改后的对象。对于iostream来说,这类函数最着名的例子可能是operator<<
和operator>>
。
让我们以iostream为例,因为它可以很好地展示我所追求的东西。例如,有时人们喜欢做的一件事是:
std::string s = (std::ostringstream() << foo << bar << baz).str();
当然这不起作用,原因有两个:
std::ostringstream()
是左值,但operator<<
将左值作为第一个参数
operator<<
会返回ostream&
(嗯,至少对于标准的basic_ostream<CharT, Traits>&
,其中CharT
和Traits
是从第一个论点)。
因此,我们假设我们想要设计插入运算符,以便上述工作(您显然不能为现有运算符执行此操作,但您可以为自己的类执行此操作)。显然,该解决方案应具有以下特征:
第一个参数可以接受左值或左值。
返回类型应该与传入的类型相同。但当然它应该只接受ostreams(即从basic_ostream
的实例化派生的类。)
虽然在这个特定用例中不需要,但我想添加第三个要求:
这个额外的规则是你可以从通过函数的rvalue中移动构造(我不知道C ++ 11流是否可移动构造,但是它是一个更通用的方案,流就像一个方便的例子。)
很明显,在C ++ 03中,并不能满足这些要求。但是,在C ++ 11中,我们有rvalue引用,这应该可以实现。
这是我对此的尝试:
#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
是否正确?
是否有更简单的方法来满足要求?
假设这确实是正确的并且是最简单的方法:你认为这样做是个好主意,还是你会反对它(特别是对于我输出的流输出,以及通过函数传递对象的更一般方案?
答案 0 :(得分:6)
std::ostringstream()
是左值,但operator<<
取左值 作为第一个论点
有一个通用插件来获取右值流,但它会返回basic_ostream<charT, traits>&
:
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);
要为您的示例正常工作,必须返回流的派生类型(std::ostringstram
)。
- 我写的输出操作符确实按预期工作(并满足我给出的所有要求)吗?或者它可能会失败 意外的方式或有其他意想不到的后果特别是:是 我在这里使用
醇>std::forward
更正了吗?
您的代码对我来说是正确的。
- 是否有更简单的方法来满足要求?
醇>
您的代码看起来类似于我为解决此问题而编写的代码(见下文)。
- 假设这确实是正确的并且是最简单的方法:你认为这样做是个好主意,还是你建议反对它 (两者都专门针对我输出的流输出,以及更多 通过函数传递对象的一般方案?
醇>
这个成语看起来很好。
在libc++中,我定义了rvalue ostream inserter,如下所示(作为扩展名)。我试图让它标准化,但是已经很晚了,委员会可以理解为没有更多的颠簸情绪:
template <class _Stream, class _Tp>
inline
typename enable_if
<
!is_lvalue_reference<_Stream>::value &&
is_base_of<ios_base, _Stream>::value,
_Stream&&
>::type
operator<<(_Stream&& __os, const _Tp& __x)
{
__os << __x;
return std::move(__os);
}
与您的不同,此仅接受右值流。但是和你的一样,它会返回具体的派生类型,因此可以使用你的例子。