将临时引用绑定到非常量引用
Bind temporary to non-const reference
Rationale
我尽量完全避免在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;
}
}
但是,这并不能处理您的情况,因为您主要关心的是获取一个字符串,而不是"处理"文件名参数。
在我自己的代码中,我使用我编写的MultiFileInputStream
,它获取构造函数中的文件名列表,并且仅在以下情况下返回 EOF最后一个已读取:如果列表为空,则读取 std::cin
。 这为您的问题提供优雅而简单的解决方案:
MultiFileInputStream in(
std::vector<std::string>( argv + 1, argv + argc ) );
std::string const input = slurp( in );
这个类值得写,因为它通常很有用,如果你经常编写类Unix实用程序。 然而,这绝对不是微不足道的,如果这是一次性需求,则可能需要做很多工作。
更通用的解决方案是基于以下事实:您可以调用非常量成员在临时上起作用,并且大多数std::istream
的成员函数返回一个std::istream&
— a非常量引用,然后绑定到非常量引用。 所以你总是可以写这样的东西:
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
派生以抛出异常,如果开放不起作用;您所需要的只是一个检查 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);
利用命名右值引用是左值的事实。
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- 编译C++时未定义的引用
- Ctypes wstring通过引用传递
- c++r值引用应用于函数指针
- 理解c++中的引用
- C++取消引用指针.为什么会发生变化
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 强制转换为引用类型
- 引用一个已擦除类型(void*)的指针
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 列出生成器(lambda的向量)会导致通过引用捕获的非常奇怪的行为
- 从对象调用成员对象,错误:引用非常量值的初始值必须是左值
- 一个非常弱的引用(无法转换为共享)
- 阻止const类函数在引用成员上调用非常常量类函数