【问题标题】:A confusing detail about the Most Vexing Parse关于最令人烦恼的解析的一个令人困惑的细节
【发布时间】:2019-05-20 07:51:08
【问题描述】:

我的问题是如何将以下行解析为函数声明:

vector<int> v(istream_iterator<int>(cin), istream_iterator<int>());

我了解 Most Vexing Parse 的大部分细节,以及为什么第二个临时迭代器可以解释为一个返回迭代器且不带参数的函数类型,但我不明白为什么第一个临时迭代器迭代器可以解释为一种类型。它代表什么类型?我的想法是它会是某种函数类型,但我看不出cin 这个名字是如何被使用的。是否声明参数是名为cinistream_iterator&lt;int&gt;?如果是这样,这是否意味着您可以任意给函数的参数名称加上括号?如果是这样,为什么?

【问题讨论】:

    标签: c++ most-vexing-parse


    【解决方案1】:

    istream_iterator&lt;int&gt;(cin)istream_iterator&lt;int&gt; cin 完全相同,但带有多余的括号。这种声明符语法继承自 C,我认为即使是 C 的发明者(Ken Thompson?)也将其描述为一个错误。

    【讨论】:

    • 作为函数的参数。
    • 我认为它们是在所问问题的上下文中(也许我不应该确切地说)。需要详细说明吗?
    • @Nawaz: istream_iterator&lt;int&gt;(cin) 没有被解释为表达式,而是作为参数声明,这就是问题的重点。
    • @Nawaz,你误会了。我没有说istream_iterator&lt;int&gt;(cin) 导致了这个问题。 OP 特别询问如何将istream_iterator&lt;int&gt;(cin) 解释为参数声明。这就是我回答的问题。
    • @Nawaz,不需要上下文。始终是cin 的声明,不能是临时对象。 You have just been wrong.
    【解决方案2】:

    我是否已经说过我喜欢 Clang(非常喜欢)?

    只需尝试以下(简化代码)

    #include <vector>
    
    void foo(std::vector<int>);
    
    int main() {
      std::vector<int> v(int(i), int());
      foo(v);
    }
    

    在新改名的LLVM Try Out 中(嗯,它只是从 llvm-gcc 变成了 clang)。

    你会得到:

    /tmp/webcompile/_21483_0.cc:6:21: warning: parentheses were disambiguated
                                               as a function declarator
      std::vector<int> v(int(i), int());
                        ^~~~~~~~~~~~~~~
    /tmp/webcompile/_21483_0.cc:7:3: error: no matching function for call to 'foo'
      foo(v);
      ^~~
    /tmp/webcompile/_21483_0.cc:3:6: note: candidate function not viable:
         no known conversion from 'std::vector<int> (int, int (*)())'
         to 'std::vector<int>' for 1st argument
    void foo(std::vector<int>);
         ^
    3 diagnostics generated.
    

    因此,@john 是对的,int(i) 被解释为int i,即函数的命名参数。

    【讨论】:

      【解决方案3】:

      是的,它是参数名称。而且,是的,您可以添加一组括号,因为有时您必须这样做。

      如果参数是函数指针,void (*f)()你需要这样写。

      编写标准的人没有花费宝贵的时间准确指出允许或实际需要括号的情况,因此标准只是说您可以拥有它们。

      【讨论】:

      • 如果有必要,您能举个例子吗?
      • int *a[10] 是一个包含 10 个 int 指针的数组,但 int (*a)[10] 是一个指向 10 个整数数组的指针(我希望我得到了正确的方法)。
      • 啊,与其说“你需要它”,不如说是“某些类型需要它。”。我将您的评论误解为“有些类型如果不包含一组额外的括号是编译时错误,否则这些括号是不必要的。”
      【解决方案4】:

      标准 (2003) 中有一个名为 Ambiguity resolution 的部分专门用于此类语法。如果您自己阅读该部分,我想我不需要进一步解释,因为它非常清楚,有很多例子!

      所以你去:

      8.2 歧义分辨率 [dcl.ambig.res]

      1 - 由于函数样式转换和 6.8 中提到的声明之间的相似性而产生的歧义也可能出现在声明的上下文中。 在这种情况下,选择是在参数名称周围带有一组冗余括号的函数声明和以函数样式强制转换作为初始值设定项的对象声明之间进行选择。就像 6.8 中提到的歧义一样,解决方案是将任何可能是声明的构造视为声明。 [注意:声明可以通过非函数样式强制转换明确消除歧义,通过 = 表示初始化或删除参数名称周围的多余括号。 ]

      [Example:
      
      struct S {
          S(int);
      };
      
      void foo(double a)
      {
         S w(int(a));  // function declaration
         S x(int());   // function declaration
         S y((int)a);  // object declaration
         S z = int(a); // object declaration
      }
      —end example]
      

      2 - 由函数样式转换和类型 ID 之间的相似性引起的歧义可能出现在不同的上下文中。歧义表现为函数式强制转换表达式和类型声明之间的选择。 解决办法是,任何在其句法上下文中可能是 type-id 的构造都应被视为 type-id。

      3- [Example:
      
      #include <cstddef>
      
      char *p;
      void *operator new(size_t, int);
      
      void foo() {
          const int x = 63;
          new (int(*p)) int; // new-placement expression
          new (int(*[x]));   // new type-id
      }
      
      //4 - For another example,
      
      template <class T>
      struct S {
          T *p;
      };
      S<int()> x;  // type-id
      S<int(1)> y; // expression (ill-formed)
      
      //5 - For another example,
      void foo()
      {
         sizeof(int(1)); // expression
         sizeof(int()); // type-id (ill-formed)
      }
      
      //6 - For another example,
      void foo()
      {
         (int(1)); //expression
         (int())1; //type-id (ill-formed)
      }
      —end example]
      

      7 - 当类型名称嵌套在括号中时,另一个歧义出现在函数声明的参数声明子句中,或者在作为 sizeof 或 typeid 运算符的操作数的类型 ID 中。 在这种情况下,选择是在声明类型为函数指针的参数和声明在 declarator-id 周围带有冗余括号的参数之间。解决方法是将类型名称视为 simple-type-specifier 而不是 declarator-id。

      [Example:
      
      class C { };
      void f(int(C)) { }    // void f(int (*fp)(C c)) { }
                            // not: void f(int C);
      int g(C);
      void foo() {
          f(1); //error: cannot convert 1 to function pointer
          f(g); //OK
      }
      
      //For another example,
      class C { };
      void h(int *(C[10]));  // void h(int *(*_fp)(C _parm[10]));
                            // not: void h(int *C[10]);
      
      —end example]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-14
        • 1970-01-01
        相关资源
        最近更新 更多