将临时绑定到非const引用

Kon*_*lph 16 c++ const temporary

合理

我会尽量避免在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]));
Run Code Online (Sandbox Code Playgroud)

为什么这不起作用?因为原型slurp需要看起来如下:

std::string slurp(std::istream&);
Run Code Online (Sandbox Code Playgroud)

也就是说,我的论证是非 - const因此我不能将它绑定到临时的.似乎没有办法使用单独的变量.

丑陋的解决方法

目前,我使用以下解决方案:

std::string input;
if (use_stdin)
    input = slurp(std::cin);
else {
    std::ifstream in(argv[1]);
    input = slurp(in);
}
Run Code Online (Sandbox Code Playgroud)

但这是以错误的方式揉搓我.首先,它是更多的代码(在SLOC中),但它也使用一个if而不是(这里)更逻辑的条件表达式,并且它在声明后使用我想要避免的赋值.

是否有一种避免这种间接初始化方式的好方法?这个问题可能会推广到需要改变临时对象的所有情况.难道流不是设计得不好以应对这种情况(const流没有意义,但是在临时流上工作确实有意义)?

Mat*_* M. 14

为什么不简单地超载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]);
}
Run Code Online (Sandbox Code Playgroud)

它是条件运算符的通用解决方案.


Jam*_*nze 11

处理时,解决方案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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这不能处理您的情况,因为您主要关注的是获取字符串,而不是"处理"文件名args.

在我自己的代码中,我使用了MultiFileInputStream我编写的,它在构造函数中获取了一个文件名列表,并且只在读取最后一个时返回EOF:如果列表为空,则读取std::cin.这为您的问题提供了一个优雅而简单的解决方案:

MultiFileInputStream in(
        std::vector<std::string>( argv + 1, argv + argc ) );
std::string const input = slurp( in );
Run Code Online (Sandbox Code Playgroud)

这个类值得写,因为如果你经常编写类似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 ) );
Run Code Online (Sandbox Code Playgroud)

但是,我认为这有点像黑客,它有一个更普遍的问题,你不能检查是否打开(由工作的构造函数调用std::ifstream.

更一般地说,虽然我理解你想要实现的目标,但我认为你会发现IO几乎总是代表一个例外.如果没有int先定义它,就无法读取,如果没有定义第一行,就无法读取std::string.我同意它并不像它那样优雅,但是,正确处理错误的代码很少像人们想的那么优雅.(这里的一个解决方案是派生自std::ifstream抛出异常,如果open不起作用;你需要的只是在构造is_open()函数体中检查 的构造函数.)