将临时绑定到非const引用

时间:2012-02-02 10:56:05

标签: 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]));

为什么这不起作用?因为slurp的原型需要如下所示:

std::string slurp(std::istream&);

即,参数i - const因此我无法将其绑定到临时。似乎没有办法使用单独的变量。

丑陋的解决方法

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

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

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

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

4 个答案:

答案 0 :(得分: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]);
}

这是条件运算符的通用解决方案。

答案 1 :(得分: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;
    }
}

但是,这不能处理您的情况,因为您主要关心的是 获取一个字符串,而不是“处理”文件名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来抛出异常 公开不起作用;所有你需要的是一个检查的构造函数 构造函数体中的is_open()。)

答案 2 :(得分:3)

所有SSA风格的语言都需要真实地使用phi节点。在任何需要根据条件值从两种不同类型构造的情况下,您都会遇到同样的问题。三元运算符无法处理此类情况。当然,在C ++ 11中还有其他技巧,比如移动流或类似的东西,或者使用lambda,而IOstreams的设计实际上与你想要做的完全相反,所以在我看来,你只需要例外。

答案 3 :(得分:1)

另一个选项可能是保存流的中间变量:

std::istream&& is = argc==1? std::move(cin) : std::ifstream(argv[1]);
std::string const input = slurp(is);

利用命名的右值引用是左值的事实。