为什么std :: istringstream似乎在三元(?:)运算符中以不同的方式解析为std :: ifstream?

时间:2014-07-11 21:00:23

标签: c++ c++11 conditional-operator

我习惯于编写带有文件名或从std::cin读取的小命令行工具,所以我使用这种模式已经有一段时间了:

int main(int argc, char* argv[])
{
    std::string filename;

    // args processing ...

    std::ifstream ifs;

    if(!filename.empty())
        ifs.open(filename);

    std::istream& is = ifs.is_open() ? ifs : std::cin;

    std::string line;
    while(std::getline(is, line))
    {
        // process line...
    }

    return 0;
}

在阅读有关Stack Overflow的问题后,我尝试修改我常用的模式,以满足从文件或std::istringstream中读取的需要。令我惊讶的是它不会编译并给出这个错误:

temp.cpp:164:47: error: invalid initialization of non-const reference of type ‘std::istream& {aka std::basic_istream<char>&}’ from an rvalue of type ‘void*’
      std::istream& is = ifs.is_open() ? ifs : iss; // won't compile

我认为它试图将std::istringstream对象(iss)转换为布尔值并获取其operator void*()

int main(int argc, char* argv[])
{
    std::string filename;
    std::string data;

    // args processing ...

    std::ifstream ifs;
    std::istringstream iss;

    if(!filename.empty())
        ifs.open(filename);

    std::istream& is = ifs.is_open() ? ifs : iss; // won't compile

    std::string line;
    while(std::getline(is, line))
    {
        // process line...
    }

    return 0;
}
  1. 为什么std::istringstreamstd::cinstd::ifstream的处理方式不同?它们都来自std::istream

    然后我记得转换了我的模式以适应三种可能性,从文件,字符串或std::cin读取。我记得那很有效(尽管它非常笨拙)。因此,对这个问题应用三重解决方案,我想出了一个完全有效的软糖:

    int main(int argc, char* argv[])
    {
        std::string filename;
        std::string data;
    
        // args processing ...
    
        std::ifstream ifs;
        std::istringstream iss;
    
        if(!filename.empty())
            ifs.open(filename);
    
        std::istream& is = ifs.is_open() ? ifs : true ? iss : std::cin; // fudge works
    
        std::string line;
        while(std::getline(is, line))
        {
            // process line...
        }
    
        return 0;
    }
    
  2. 为什么这个软糖有效? GCC是否违反了关于三元运算符(?:)如何解析其类型的规则?或者我错过了什么?

3 个答案:

答案 0 :(得分:14)

如果您有基类和派生类,则三元条件运算符知道将派生类转换为基类。但是如果你有两个派生类,它们不知道将它们转换为它们的公共基类。这不是gcc的表现;这就是指定三元条件运算符如何在标准中工作。

std::istream& is = ifs.is_open() ? ifs : std::cin;

这很好,因为std::cin的类型为std::istreamstd::ifstream是基类。

std::istream& is = ifs.is_open() ? ifs : iss; // won't compile

这不起作用,因为std::ifstreamstd::istringstream&#34;仅限&#34;有一个共同的基类。

std::istream& is = ifs.is_open() ? ifs : true ? iss : std::cin; // fudge works

这是有效的,因为它被解析为:

std::istream& is = ifs.is_open() ? ifs : (true ? iss : std::cin);

,带括号的表达式的类型为std::istream。因此iss会被转换为std::istream类型的左值,如果选中,ifs也会被转换为类似。

答案 1 :(得分:6)

编译器尝试从三元运算符中找到两个结果的公共类型,如果您看到例如this reference您会看到有一个casting operator override for void* (or bool for C++11 and later),因此编译器会使用它。

但是当它尝试执行赋值时,它会输出错误,因为在初始化的右侧,您有void*(或者bool)类型,并且在左侧这边是std::istream的引用。

要解决此问题,您必须手动将每个流投射到std::istream的引用,例如static_cast

答案 2 :(得分:1)

gcc抱怨,因为ifs和iss是两种不同的类型。 static_casting类型为std :: istream&amp;将解决您的问题。