为什么std :: istringstream似乎在三元(?:)运算符中不同地解析为std :: ifstream? [英] Why does std::istringstream appear to resolve differently to std::ifstream in the ternary (?:) operator?

查看:374
本文介绍了为什么std :: istringstream似乎在三元(?:)运算符中不同地解析为std :: ifstream?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我习惯于写一些小命令行工具,这些工具需要一个文件名或读取 std :: cin ,所以我一直使用这个模式一段时间:

I am used to writing little command line tools that take either a file name or read from std::cin, so I have been using this pattern for quite a while:

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 读取的需要。令我惊讶的是它不会编译并给出这个错误:

After reading a question on Stack Overflow, I tried to modify my usual pattern to suit the need to read either from a file or from a std::istringstream. To my surprise it won't compile and gives this error:


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 )转换为布尔值并获取其运算符void *()

Which looks to me like it's trying to convert the std::istringstream object (iss) to a boolean and getting its 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. c $ c> std :: istringstream 不同于 std :: cin std :: ifstream ?他们都来自 std :: istream

  1. Why is it treating std::istringstream differently from std::cin and std::ifstream? They all derive from std::istream.

然后我记得转换我的模式以适应三种可能性,从文件,字符串或 std :: cin 。我记得工作(虽然它是相当笨拙)。所以应用三重解决方案这个问题我想出了一个完全工作的软糖:

Then I remembered having converted my pattern to accommodate three possibilities, reading from file, string or std::cin. And I remember that worked (although it's pretty clumsy). So applying the triple solution to this problem I came up with a fudge that totally works:

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;
}


  • 为什么这个fudge工作? GCC是否违反了三元运算符(?:)如何解析其类型的任何规则?

  • Why does this fudge work? Is GCC breaking any rules on how the ternary operator (?:) resolves its types? Or am I missing something?


    推荐答案

    最小化示例:

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

    Clang报告:

    main.cpp:13:19: error: incompatible operand types ('B' and 'C')
        A& refA = true? b : c;
    

    相关规则在标准的§5.16[expr.cond] / p3-6中找到:

    The relevant rule is found in §5.16 [expr.cond]/p3-6 of the standard:


    3否则,如果第二个和第三个操作数有不同的类型,
    有(可能是cv限定的)类类型,或者如果两个都是相同值类别的glvalues
    和除了
    cv-qualification之外的相同类型,则尝试将这些操作数
    中的每一个转换为另一个的类型。确定类型T1的
    操作数表达式E1是否可以转换为匹配类型T2的操作数
    表达式E2的过程定义如下:

    3 Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:


    • 如果E2是一个左值:如果E1可以隐式转换(第4条)到E1的类型lvalue reference T2,
      可以被转换为匹配E2

    • 如果E2是xvalue:如果E1可以被转换为E1,则E1可以转换为匹配E2隐式转换为类型rvalue reference to T2,受限于
      引用必须直接绑定的约束。

    • 如果E2是prvalue或如果上述两种转换可以完成,并且至少一个操作数具有(可能是cv限定的)
      类类型:

      • 如果E1和E2具有类类型,类型是相同的或者一个是另一个的基类:如果T2的类型是相同类型,或者基类类型是
        ,则T1可以转换为
        匹配E2 ,并且T2的cv-qualification与T1的
        cv-qualification相同,或者是更高的cv-qualification。如果应用转换,则通过从
        E1复制初始化类型T2的临时,并使用该临时作为转换的操作数,将E1改变为类型T2的
        a prvalue。

      • 否则(即,如果E1或E2具有非类类型,或者如果它们都具有类类型,但是底层类不是相同的或者
        一个是另一个的基类):E1可以转换以匹配E2,如果如果E2被转换为prvalue(或者如果E2是
        prvalue,则它具有的类型),如果E1
        可以隐式地转换为表达式E2将具有
        的类型。

      • If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (Clause 4) to the type "lvalue reference to T2", subject to the constraint that in the conversion the reference must bind directly (8.5.3) to an lvalue.
      • If E2 is an xvalue: E1 can be converted to match E2 if E1 can be implicitly converted to the type "rvalue reference to T2", subject to the constraint that the reference must bind directly.
      • If E2 is a prvalue or if neither of the conversions above can be done and at least one of the operands has (possibly cv-qualified) class type:
        • if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1. If the conversion is applied, E1 is changed to a prvalue of type T2 by copy-initializing a temporary of type T2 from E1 and using that temporary as the converted operand.
        • Otherwise (i.e., if E1 or E2 has a nonclass type, or if they both have class types but the underlying classes are not either the same or one a base class of the other): E1 can be converted to match E2 if E1 can be implicitly converted to the type that expression E2 would have if E2 were converted to a prvalue (or the type it has, if E2 is a prvalue).

        使用此过程,确定第二个操作数是否可以是
        转换为匹配第三个操作数,以及第三个操作数
        是否可以转换为匹配第二个操作数。如果两者都可以
        转换,或者一个可以转换,但转换不明确,
        程序是不成形的。如果两者都不能转换,则操作数
        保持不变,并且如下所述进行进一步检查
        。如果只有一个转换是可能的,那么转换是
        应用于所选择的操作数,转换后的操作数用于本节其余部分的原始操作数的
        中。

        Using this process, it is determined whether the second operand can be converted to match the third operand, and whether the third operand can be converted to match the second operand. If both can be converted, or one can be converted but the conversion is ambiguous, the program is ill-formed. If neither can be converted, the operands are left unchanged and further checking is performed as described below. If exactly one conversion is possible, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this section.

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

        4 If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category and it is a bit-field if the second or the third operand is a bit-field, or if both are bit-fields.

        5否则结果是prvalue。如果第二和第三操作数
        不具有相同的类型,并且具有(可能是cv限定的)
        类类型,则使用重载分辨率来确定转换
        (如果有的话)应用于操作数(13.3.1.2,13.6)。如果
        重载解析失败,程序是不成形的。否则,应用由此确定的$​​ b $ b转换,并且使用转换的操作数
        代替该
        部分的剩余部分的原始操作数。

        5 Otherwise, the result is a prvalue. If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this section.

        6在
        的第二和第三个值上执行标准转换(Lvalue-to-Rvalue)(4.1​​),数组到指针(4.2)和
        函数到指针操作数。在这些转换之后,下面的
        之一将保持:

        6 Lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the second and third operands. After those conversions, one of the following shall hold:


        • 第二个和第三个操作数具有相同的类型;结果是那种类型。如果操作数具有类类型,结果是结果类型的prvalue
          临时,它是从
          第二个操作数或第三个操作数复制初始化的,取决于
          的值

        • 第二个和第三个操作数具有算术或枚举类型。

        • 第二个和第三个操作数中的一个或两个具有指针类型;指针转换(4.10)和资格转换(4.4)是
          执行,使他们成为他们的复合指针类型(第5条)。
          结果是复合指针类型。

        • 第二个和第三个操作数中的一个或两个都有指向成员类型的指针;指向成员转换(4.11)和资格
          转换(4.4)的指针被执行以使它们成为它们的复合
          指针类型(第5条)。结果是复合指针类型。

        • 第二个和第三个操作数都有类型 std :: nullptr_t 另一个是空指针常量。结果是
          类型 std :: nullptr_t

        • The second and third operands have the same type; the result is of that type. If the operands have class type, the result is a prvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand.
        • The second and third operands have arithmetic or enumeration type; the usual arithmetic conversions are performed to bring them to a common type, and the result is of that type.
        • One or both of the second and third operands have pointer type; pointer conversions (4.10) and qualification conversions (4.4) are performed to bring them to their composite pointer type (Clause 5). The result is of the composite pointer type.
        • One or both of the second and third operands have pointer to member type; pointer to member conversions (4.11) and qualification conversions (4.4) are performed to bring them to their composite pointer type (Clause 5). The result is of the composite pointer type.
        • Both the second and third operands have type std::nullptr_t or one has that type and the other is a null pointer constant. The result is of type std::nullptr_t.

        关键点是,这将总是尝试转换一个操作数匹配另一个的类型,而不是转换为第三种类型,直到你打5段,在这一点编译器开始寻找用户定义的隐式转换为指针或算术类型(这些只是对运算符的内置候选函数的可能参数:在第13.6节中定义),以及

        The crucial point is that this will always attempt to convert one operand to match the type of the other, rather than convert both to a third type, until you hit paragraph 5, at which point the compiler starts looking for user-defined implicit conversions to pointer or arithmetic types (those are only possible arguments to the built-in candidate functions for operator?: defined in §13.6), and for your purposes, you really don't want it to get there.

        在最小化的示例中,这与您的错误情况直接相关( A = istream B = ifstream C = istringstream ),将一个转换为另一个的类型是不可能的,直到p5,编译器会查找用户定义的隐式转换。在最小化的示例中,没有转换,重载解析失败,整个事情是不成型的。在你的错误情况下,pre-C ++ 11(和libstdc ++ post-C ++ 11,显然)有一个从流到 void * 的隐式转换,所以编译器这样做,给整个表达式 void * 类型,但是显然不能绑定到 std :: istream

        In the minimized example, which correlates directly to your error case (A = istream, B = ifstream, C = istringstream), converting one to the type of the other is not possible, and so the logic goes down to p5, and the compiler looks for user-defined implicit conversions. In the minimized example there's no conversion, overload resolution fails, and the whole thing is ill-formed. In your error case, pre-C++11 (and in libstdc++ post-C++11, apparently) there's an implicit conversion from a stream to void *, so the compiler does that, giving the whole expression a void * type, but that obviously can't bind to a reference to std::istream, so that's the error you see.

        在第二种情况下:

        ifs.is_open() ? ifs : true ? iss : std::cin;
        

        std :: cin c $ c> std :: istream std :: istringstream 可以转换为其基类 std :: istream ,因此内部条件表达式格式良好,并且具有类型 std :: istream 。然后使用外部条件表达式,第二个操作数 std :: ifstream 的类型可转换为第三个操作数的类型 std :: istream ,因此整个表达式的格式正确,并且具有绑定到引用的正确类型。

        std::cin has type std::istream, and std::istringstream can be converted to its base class std::istream, so the inner conditional expression is well-formed and has type std::istream. Then with the outer conditional expression, again the type of the second operand, std::ifstream, is convertible to the type of the third operand, std::istream, so the entire expression is well-formed and has the right type to bind to the reference.

        这篇关于为什么std :: istringstream似乎在三元(?:)运算符中不同地解析为std :: ifstream?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆