Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么std::istringstream在三元(?:)运算符中的解析方式与std::ifstream不同?_C++_C++11_Conditional Operator - Fatal编程技术网

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

C++ 为什么std::istringstream在三元(?:)运算符中的解析方式与std::ifstream不同?,c++,c++11,conditional-operator,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&

我习惯于编写小的命令行工具,这些工具要么使用文件名,要么从
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;
}
在阅读了一个关于堆栈溢出的问题后,我试图修改我的常用模式,以满足从文件或
std::istringstream
读取的需要。令我惊讶的是,它无法编译,并出现以下错误:

  • 为什么它对待
    std::istringstream
    的方式不同于
    std::cin
    std::ifstream
    ?它们都源自
    std::istream

    std::istream& is = ifs.is_open() ? ifs : iss; // won't compile
    
    然后我记得我已经转换了模式以适应三种可能,从文件、字符串或
    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;
    }
    
  • 为什么这种软糖有效?GCC是否违反了三元运算符(
    ?:
    )解析其类型的规则?还是我遗漏了什么


  • 编译器试图从三元运算符中为这两个结果找到一个公共类型,如果您看到,例如,您将看到有一个,因此编译器使用该类型

    但是当它尝试执行赋值时,它会出错,因为在初始化的右侧有一个
    void*
    (或者
    bool
    )类型,在左侧有一个对
    std::istream
    的引用

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

    要解决此问题,您必须手动将每个流转换为对
    std::istream
    的引用,例如
    static\u cast

    gcc正在抱怨,因为ifs和iss是两种不同的类型。静态\u将类型转换为std::istream&将解决您的问题。

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

    std::istream& is = ifs.is_open() ? ifs : std::cin;
    
    这很好,因为
    std::cin
    具有类型
    std::istream
    ,它是
    std::ifstream
    的基类

    std::istream& is = ifs.is_open() ? ifs : iss; // won't compile
    
    这不起作用,因为
    std::ifstream
    std::istringstream
    “only”有一个公共基类

    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
    也将被类似地转换。

    示例:

    class A { };
    class B : public A { };
    class C : public A { };
    
    int main() {
        B b;
        C c;
        A& refA = true? b : c;
    }
    
    叮当声报道:

    main.cpp:13:19: error: incompatible operand types ('B' and 'C')
        A& refA = true? b : c;
    
    相关规则见本标准§5.16【解释条件】/p3-6:

    3否则,如果第二个和第三个操作数具有不同的类型和 具有(可能是cv限定的)类类型,或者两者都是glvalues 具有相同的价值类别和相同的类型,但 cv限定,尝试转换每个操作数 与另一个的类型相同。用于确定是否存在以下情况的过程: T1类型的操作数表达式E1可以转换为与操作数匹配 T2型表达式E2定义如下:

    • 如果E2是左值:如果E1可以隐式转换(第4条)为类型“对T2的左值引用”,则E1可以转换为匹配E2, 受限于转换中引用必须 直接绑定(8.5.3)到左值
    • 如果E2是一个xvalue:如果E1可以隐式转换为类型“rvalue reference to T2”,则可以将E1转换为与E2匹配的值,但需遵守以下条件: 引用必须直接绑定的约束
    • 如果E2是prvalue,或者如果上述两种转换都无法完成,并且至少有一个操作数具有(可能是cv限定的) 班级类型:
      • 如果E1和E2具有类类型,并且基础类类型相同,或者其中一个是另一个的基类:E1可以转换为 如果T2的类与的类型相同,或是的基类,则匹配E2, T1的等级与T2的cv资格相同 简历资格为或大于 T1的简历资格。如果应用了转换,E1将更改为 通过从中复制初始化T2类型的临时变量来获取T2类型的PR值 E1,并使用该临时值作为转换的操作数
      • 否则(即,如果E1或E2具有非类类型,或者如果它们都具有类类型,但基础类既不相同也不相同) 一个是另一个的基类):如果E1 可以隐式转换为表达式E2将具有的类型 如果E2被转换为prvalue(或其类型,如果E2是 prvalue)
    使用此过程,确定是否可以使用第二个操作数 转换为匹配第三个操作数,以及第三个操作数 可以转换为与第二个操作数匹配。如果两者都可以 转换,或者一个可以转换,但转换不明确, 这个程序格式不好。如果两者都不能转换,则操作数 保持不变,并按说明执行进一步检查 在下面如果只可能进行一次转换,则该转换为 应用于选定的操作数,并在 本节剩余部分的原始操作数的位置

    4如果第二个和第三个操作数是相同值的值 类别和具有相同的类型,则结果为该类型和值 类别,如果第二个或第三个操作数是 位字段,或者如果两者都是位字段


    5否则,结果为PRV值。如果第二个和第三个操作数 不具有相同的类型,并且其中一个具有(可能符合cv条件) 类类型,重载解析用于确定co
    ifs.is_open() ? ifs : true ? iss : std::cin;