如何以允许传递临时变量的方式将 std::istream 传递给函数?
How can you pass an std::istream into a function in a way that allows to pass temporaries?
我正在尝试创建一个构造函数来从给定的任何 istream 加载资源。我似乎无法找出将 istream 参数传递给构造函数的最佳方法。
Loader::Loader(istream stream);
由于对象切片,这个显然很糟糕,所以别无选择。
Loader::Loader(istream& stream);
这就是我现在使用的,看起来还不错。但是它有一个重要的问题——你不能给它一个临时的,因为临时的不能绑定到非常量引用!例如,以下将不起作用:
Container():
mLoader(ifstream("path/file.txt", ios::binary)
{
}
这是一个相当大的限制,因为我现在被迫将 ifstream 存储为 Container 的成员变量,以延长其生命周期。
由于问题出在非 const 引用上,因此可以这样想:
Loader::Loader(const istream& stream);
但是由于 .seek() 等是非常量,这也不是一个选项...
那么,如何巧妙的解决这个问题呢?
例子
Container():
mLoader(ifstream("path/file.txt", ios::binary)
{
}
… 构思不当,因为临时对象的生命周期只是构造函数调用。然后你会留下一个 悬空引用 。使用该引用将是未定义的行为。
但是,从技术上讲,为了能够将临时参数传递给非 const
形式参数的引用,只需像这样定义一个小助手:
template< class Type >
auto temp_ref( Type&& o )
-> Type&
{ return o; }
然后你可以打电话给foo( temp_ref( Whatever() ) )
,但请记住,该临时对象的生命周期是它出现的完整表达式。
如果您的编译器是 c++11 或更高版本,您可以简单地提供一个将 istream
作为右值引用的构造函数版本:
void Loader::Loader(std::istream&& is)
快速示例:
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
struct Loader
{
Loader(std::istream& is)
{
read(is);
}
Loader(std::istream&& is)
{
read(is);
}
void read(std::istream& is)
{
is >> std::quoted(x);
is >> std::quoted(y);
}
std::string x, y;
};
std::ostream& operator<<(std::ostream& os, const Loader& l)
{
os << "x = " << l.x;
os << ", y = " << l.y;
return os;
}
auto main() -> int
{
using namespace std;
Loader l(istringstream(R"text("donkey" "horse")text"));
cout << l << endl;
istringstream not_temp(R"text("apple" "banana")text");
Loader l2(not_temp);
cout << l2 << endl;
return 0;
}
预期输出:
x = donkey, y = horse
x = apple, y = banana
将指针传递给新创建的对象是可行的。
Loader(istream * pstream)
{
try
{
// ......
delete pstream;
throw;
}
catch(...)
{
delete pstream;
}
}
Container():
mLoader(new ifstream("path/file.txt", ios::binary))
{
}
我正在尝试创建一个构造函数来从给定的任何 istream 加载资源。我似乎无法找出将 istream 参数传递给构造函数的最佳方法。
Loader::Loader(istream stream);
由于对象切片,这个显然很糟糕,所以别无选择。
Loader::Loader(istream& stream);
这就是我现在使用的,看起来还不错。但是它有一个重要的问题——你不能给它一个临时的,因为临时的不能绑定到非常量引用!例如,以下将不起作用:
Container():
mLoader(ifstream("path/file.txt", ios::binary)
{
}
这是一个相当大的限制,因为我现在被迫将 ifstream 存储为 Container 的成员变量,以延长其生命周期。
由于问题出在非 const 引用上,因此可以这样想:
Loader::Loader(const istream& stream);
但是由于 .seek() 等是非常量,这也不是一个选项...
那么,如何巧妙的解决这个问题呢?
例子
Container():
mLoader(ifstream("path/file.txt", ios::binary)
{
}
… 构思不当,因为临时对象的生命周期只是构造函数调用。然后你会留下一个 悬空引用 。使用该引用将是未定义的行为。
但是,从技术上讲,为了能够将临时参数传递给非 const
形式参数的引用,只需像这样定义一个小助手:
template< class Type >
auto temp_ref( Type&& o )
-> Type&
{ return o; }
然后你可以打电话给foo( temp_ref( Whatever() ) )
,但请记住,该临时对象的生命周期是它出现的完整表达式。
如果您的编译器是 c++11 或更高版本,您可以简单地提供一个将 istream
作为右值引用的构造函数版本:
void Loader::Loader(std::istream&& is)
快速示例:
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
struct Loader
{
Loader(std::istream& is)
{
read(is);
}
Loader(std::istream&& is)
{
read(is);
}
void read(std::istream& is)
{
is >> std::quoted(x);
is >> std::quoted(y);
}
std::string x, y;
};
std::ostream& operator<<(std::ostream& os, const Loader& l)
{
os << "x = " << l.x;
os << ", y = " << l.y;
return os;
}
auto main() -> int
{
using namespace std;
Loader l(istringstream(R"text("donkey" "horse")text"));
cout << l << endl;
istringstream not_temp(R"text("apple" "banana")text");
Loader l2(not_temp);
cout << l2 << endl;
return 0;
}
预期输出:
x = donkey, y = horse
x = apple, y = banana
将指针传递给新创建的对象是可行的。
Loader(istream * pstream)
{
try
{
// ......
delete pstream;
throw;
}
catch(...)
{
delete pstream;
}
}
Container():
mLoader(new ifstream("path/file.txt", ios::binary))
{
}