【问题标题】:C++: bizarre occurrence of "Request for member X of Y which is of non-class type Z"C++:“请求 Y 的非类类型 Z 的成员 X”的奇怪出现
【发布时间】:2012-06-21 12:49:16
【问题描述】:

以下程序,用 g++ 4.6 编译,产生错误

request for member ‘y’ in ‘a2’, which is of non-class type ‘A<B>(B)’

在最后一行:

#include <iostream>

template <class T> class A
{
public:
  T y;
  A(T x):y(x){}
};

class B
{
public:
  int u;
  B(int v):u(v){}
};

int main()
{
  int v = 10;
  B b1(v);

  //works
  A<B> a1(b1);

  //does not work (the error is when a2 is used)
  A<B> a2(B(v));

  //works
  //A<B> a2((B(v)));

  std::cout << a1.y.u << " " << a2.y.u << std::endl;    
}

从代码中包含的工作变体可以看出,在 A 的构造函数的参数周围添加括号可以解决问题。

我看到了一些由构造函数调用解释为函数声明引起的相关错误,例如在创建一个没有构造函数参数但带有大括号的对象时:

myclass myobj();

但在我看来

A<B> a2(B(v));

不能解释为函数声明。

有人可以向我解释发生了什么吗?

【问题讨论】:

标签: c++ most-vexing-parse


【解决方案1】:

这是一种最令人头疼的解析,编译器将A&lt;B&gt; a2(B(v)) 解释为函数的声明。这样:

A&lt;B&gt; 是返回类型
a2 是函数名称
B 是参数类型
v 是参数名称

所以,当你在做的时候

std::cout << a1.y.u << " " << a2.y.u << std::endl;

编译器不会将a2.y.u 视为一个类,这就是您收到non-class type 错误的原因。

此外,由于函数声明中不允许使用双括号A&lt;B&gt; a2((B(v))); 版本可以工作,因为编译器不再将其解释为函数声明,而是作为变量声明。

【讨论】:

  • 一个更有趣的问题是为什么v是一个整数变量,表达式A&lt;B&gt; a2(B(v));可以被解析为一个函数声明......(这实际上是问的问题)--它最后的 cmets 似乎很清楚,用户已经知道这是一个最棘手的解析案例,但想知道编译器是如何到达那里的。
  • @DavidRodríguez-dribeas 你说得对,我添加了编译器如何解释该行本身。
  • 哇,我没有意识到最令人烦恼的解析会变得如此糟糕。 int foo(int (bar)) { return 0; } 为什么允许在参数名称周围加上括号?我原以为最麻烦的解析只发生在初始化器可以被解析为函数类型时。现在我看到了,因为参数声明可以在参数名称周围加上括号,所以比这更糟糕。我想我很惊讶他们没有继续为函数声明提供“消除歧义”括号,因此我们无法消除歧义:int foo((int (bar))) { return 0; }
  • 显然允许使用括号,因为它们在普通变量声明中是允许的:int main() { int (bar) = 0; } 为什么?
  • @bames53 与 int (*f)() 中需要它们来声明函数指针的方式相同,我认为在其他情况下需要统一允许它们。
【解决方案2】:

我认为您被“最令人烦恼的解析”所困扰,这意味着 A&lt;B&gt; a2(B(v)); 被解析为函数声明而不是变量声明。

【讨论】:

    【解决方案3】:

    这是一个函数声明:

    A<B> a2(B(v));
    //is same as:
    A<B> a2(B v);
    
    //consider:
    int foo(int v);
    int foo(int (v));
    

    【讨论】:

      【解决方案4】:

      如以下代码示例所示:

      int a (int(v)) {
          return v;
      }
      
      int main() {
          std::cout << a(5); //prints 5
      }
      

      那行确实是一个声明。在此示例中,参数的类型为 int 并命名为 v。与您的代码相关的是,参数的类型为B,并命名为v。这就是为什么在使用双括号时会出现类似行为的原因:因为它是一回事!

      【讨论】:

      • +1 最后,一个解释为什么的答案可以被解析为函数声明。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      • 1970-01-01
      • 1970-01-01
      • 2014-12-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多