![](/img/trans.png)
[英]non-const lvalue reference to type '' cannot bind to a temporary of type ' *'
[英]Bind temporary to non-const reference
我会尽量避免在C ++代码分配完全 。 也就是说,我只使用初始化并尽可能将局部变量声明为const
(即除了循环变量或累加器之外)。
现在,我发现了一个不起作用的情况。 我认为这是一般模式,但特别是在以下情况下出现:
假设我有一个程序将输入文件的内容加载到字符串中。 您可以通过提供文件名( tool filename
)或使用标准输入流( cat filename | tool
)来调用该工具。 现在,我如何初始化字符串?
以下不起作用:
bool const use_stdin = argc == 1;
std::string const input = slurp(use_stdin ? static_cast<std::istream&>(std::cin)
: std::ifstream(argv[1]));
为什么这不起作用? 因为slurp
的原型需要看起来如下:
std::string slurp(std::istream&);
也就是说,参数我是非 const
,因此我不能将它绑定到临时的。 似乎没有办法使用单独的变量。
目前,我使用以下解决方案:
std::string input;
if (use_stdin)
input = slurp(std::cin);
else {
std::ifstream in(argv[1]);
input = slurp(in);
}
但这是以错误的方式揉搓我。 首先,它是更多的代码(在SLOC中),但它也使用if
而不是(此处)更逻辑的条件表达式,并且它在声明之后使用我想要避免的赋值。
是否有一种避免这种间接初始化方式的好方法? 问题可能会推广到需要改变临时对象的所有情况。 难道流不是设计得不好以应对这种情况( const
流没有意义,但是在临时流上工作确实有意义)?
为什么不简单地超载slurp
?
std::string slurp(char const* filename) {
std::ifstream in(filename);
return slurp(in);
}
int main(int argc, char* argv[]) {
bool const use_stdin = argc == 1;
std::string const input = use_stdin ? slurp(std::cin) : slurp(argv[1]);
}
它是条件运算符的通用解决方案。
使用if
的解决方案或多或少是处理argv
时的标准解决方案:
if ( argc == 1 ) {
process( std::cin );
} else {
for ( int i = 1; i != argc; ++ i ) {
std::ifstream in( argv[i] );
if ( in.is_open() ) {
process( in );
} else {
std::cerr << "cannot open " << argv[i] << std::endl;
}
}
但是,这不能处理您的情况,因为您主要关注的是获取字符串,而不是“处理”文件名args。
在我自己的代码中,我使用了我编写的MultiFileInputStream
,它在构造函数中获取了一个文件名列表,并且只在读取最后一个时返回EOF:如果列表为空,则读取std::cin
。 这为您的问题提供了一个优雅而简单的解决方案:
MultiFileInputStream in(
std::vector<std::string>( argv + 1, argv + argc ) );
std::string const input = slurp( in );
这个类值得写,因为如果你经常编写类似Unix的实用程序,它通常很有用。 然而,这绝对不是微不足道的,如果这是一次性的需要,可能需要做很多工作。
更通用的解决方案是基于以下事实:您可以在临时调用非const成员函数,并且std::istream
大多数成员函数返回std::istream&
-a非const-reference这一事实然后将绑定到非const引用。 所以你总是可以这样写:
std::string const input = slurp(
use_stdin
? std::cin.ignore( 0 )
: std::ifstream( argv[1] ).ignore( 0 ) );
我认为这有点像黑客攻击,它有一个更普遍的问题,你不能检查是否打开(由std::ifstream
的构造函数调用。
更一般地说,虽然我理解你想要实现的目标,但我认为你会发现IO几乎总是代表一个例外。 如果没有先定义它,就无法读取int
如果没有先定义std::string
,就无法读取一行。 我同意它并不像它那样优雅,但是,正确处理错误的代码很少像人们想的那么优雅。 (这里的一个解决方案是从std::ifstream
派生一个异常,如果open不起作用;你需要的只是一个在构造函数体中检查is_open()
的构造函数。)
所有SSA风格的语言都需要实际上可以使用phi节点。 在任何需要根据条件值从两种不同类型构造的情况下,您都会遇到同样的问题。 三元运算符无法处理此类情况。 当然,在C ++ 11中还有其他技巧,比如移动流或类似的东西,或者使用lambda,而IOstreams的设计实际上与你想要做的完全相反,所以在我看来,你只需要例外。
另一个选项可能是保存流的中间变量:
std::istream&& is = argc==1? std::move(cin) : std::ifstream(argv[1]);
std::string const input = slurp(is);
利用命名的右值引用是左值的事实。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.