我正在考虑一个与完美转发有一些相似性的问题,但是函数参数没有传递给被调用函数,而是返回.这就是我称之为"完美传递"的原因.
问题如下:
假设我们有一个函数,它通过引用获取一个对象(可能还有一些额外的参数),修改该对象,并返回修改后的对象.这些函数的最着名的例子可能是operator<<和operator>>iostreams一样.
让我们以iostream为例,因为它可以很好地展示我所追求的东西.例如,有时人们喜欢做的一件事是:
std::string s = (std::ostringstream() << foo << bar << baz).str();
Run Code Online (Sandbox Code Playgroud)
当然这不起作用,原因有两个:
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;
}
Run Code Online (Sandbox Code Playgroud)
它确实用gcc编译(使用选项-std=c++0x),并在运行时SomeType按预期输出.然而,到达那里是一个非常复杂的机器.
因此我的问题:
我写的输出操作符确实按预期工作(并满足我给出的所有要求)?或者它可能以意想不到的方式失败或产生其他意外后果?特别是:我std::forward在这里使用正确吗?
有没有更简单的方法来满足要求?
假设这确实是正确的并且是最简单的方法:你认为这样做是个好主意,还是你会建议它(特别是对于我的例子中的流输出,以及作为传递对象的更一般方案)通过功能)?
std::ostringstream()是一个右值,但是operator<<将左值作为第一个参数
有一个通用插入器来获取rvalue流,但它返回一个basic_ostream<charT, traits>&:
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);
Run Code Online (Sandbox Code Playgroud)
要为您的示例正常工作,它必须返回stream(std::ostringstram)的派生类型.
- 我写的输出操作符确实按预期工作(并满足我给出的所有要求)?或者它可能以意想不到的方式失败或产生其他意外后果?特别是:我
std::forward在这里使用正确吗?
你的代码对我来说是正确的.
- 有没有更简单的方法来满足要求?
您的代码看起来类似于我为解决此问题而编写的代码(见下文).
- 假设这确实是正确的并且是最简单的方法:你认为这样做是个好主意,还是你会建议它(特别是对于我的例子中的流输出,以及作为传递对象的更一般方案)通过功能)?
这个成语对我来说很好看.
在libc ++中,我定义了rvalue ostream插入器,如下所示(作为扩展).我试图让它标准化,但是已经很晚了,委员会可以理解为没有更多的颠簸情绪:
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);
}
Run Code Online (Sandbox Code Playgroud)
与您的不同,这只接受右值流.但是和你的一样,它返回具体派生类型,因此适用于你的例子.