【问题标题】:C++ class member name lookup issues (regarding the wording of standard n3225)C++ 类成员名称查找问题(关于标准 n3225 的措辞)
【发布时间】:2011-01-20 01:49:10
【问题描述】:

我对标准 10.2/13 很困惑,

[注意:即使名称查找的结果是明确的,使用在多个子对象中找到的名称可能仍然是不明确的(4.11、5.2.5、5.3.1、11.2)。-结束注释] [示例:

struct B1 {
  void f();
  static void f(int);
  int i;
};
struct B2 {
  void f(double);
};
struct I1: B1 { };
struct I2: B1 { };
struct D: I1, I2, B2 {
  using B1::f;
  using B2::f;
  void g() {
    f(); // Ambiguous conversion of this
    f(0); // Unambiguous (static)
    f(0.0); // Unambiguous (only one B2)
    int B1::* mpB1 = &D::i; // Unambiguous
    int D::* mpD = &D::i; // Ambiguous conversion
  }
};

我不明白为什么这是明确的 int B1::* mpB1 = &D::i; // 明确的

Visual C++、Gcc 和 CLang 都说它是对 D::i 的模棱两可的访问!

措辞似乎与核心问题 #39 有关 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#39,最终提案在这里:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1626.pdf

我现在发现新的基于算法的措辞 (10.2/3-10.2/6) 更加令人困惑,因为 10.2/9、10.2/10、10.2/11 和 10.2/13 中的注释都没有完全符合到 10.2/3-10.2/6。我可以将 10.2/9-10.2/11 作为例外,但我对 10.2/13 尤其感到困惑。我不知道 10.2/13 的意图。

10.2/13中的例子应该如何按照10.2/3-10.2/6查找? 10.2/13的意图是什么,即10.2/13被认为是10.2/3-10.2/6的例外是什么情况?

请给我一些提示。非常感谢。


经过一番思考,我觉得 10.2/13 的意图对我来说更清楚了。

int B1::* mpB1 = &D::i; // 明确的

这应该是明确的,当前的编译器在这方面是错误的。这是明确的,因为指向类成员初始化的指针不涉及访问对象。

int D::* mpD = &D::i; // 模棱两可的转换

这实际上意味着当从 int B1::*mpB1 转换为 int D::*mpD 时,由于基类不明确,转换是不明确的。

【问题讨论】:

标签: c++ c++11 name-lookup


【解决方案1】:

对于 B1::* 的情况,解释是明确的,只是从 B1 的开头到 i 的偏移量。

在 5.3.1/3 中:

struct A { int i; };
struct B : A { };
... &B::i ... // has type int A::*

所以诀窍是首先让 &D::i 成为 B1::* 类型。那么:

int B1::* mpB1 = &D::i; // Unambiguous

很简单。然后兴趣就来了:

int D::* mpD = &D::i; // Ambiguous conversion

这里的 RHS 是 B1::* 类型,需要进行转换,因为我们需要确定所引用的基数。

【讨论】:

  • +1 表示最重要的一点——我从中了解到:&D::i 的类型为 int B1::*,而不是 int D::*。该标准的引用将在 §4.11/2 中。很挑剔,由于菱形,转换并不模糊,因为示例中不存在菱形:D 中有两个基本的B1 子对象。如果有钻石,转换就不会模棱两可(D 会有一个 B1 子对象)不过,我认为根据 §4.11/2,它仍然是不正确的。
【解决方案2】:

这个:

int B1::* mpB1 = &D::i; // Unambiguous

是明确的,因为结果被分配给 B 类的 pointer to member
因此,选择哪个 i 并不重要,因为偏移量是相对于 B 成员(不是父 D 类)。

所以这对你我来说是明确的,但我认为编译器无法处理它。

【讨论】:

  • 我确实赞成这个答案,但现在我觉得它缺少@Keith 答案中存在的重要一点:&D::i 的结果是int B1::* 类型,它是从B1 基地。歧义在于分配所需的转换。标准中的引用将在 §4.11/2 中。
  • "所以选择哪个 i 并不重要,因为偏移量是相对于 B 成员的" 抱歉,这没有意义。
  • @curiousguy:唯一的iB1 内。由于我们正在获取成员的地址,因此我们正在获取该成员在类中的偏移量。因此,在明确的情况下,只有一个值可供选择。 i 到 B1 底边的距离。在第二种情况下,我们将结果分配为 ​​D 指针的偏移量,因此实际上 D 中 i 的偏移量有四种不同的结果。
  • @LokiAstari "因为我们得到了成员的地址,所以我们得到了成员在类中的偏移量。" 这只是一个实现细节。问题是名称查找:D 中的哪个成员 i? “在第二种情况下,我们将结果分配为 ​​D 指针的偏移量,因此实际上在 D 中 i 的偏移量有四种不同的结果。” 为什么是四种?
  • @curiousguy:4 我的意思是“超过 1”
【解决方案3】:

对 ISO IEC 14882 2003 第 10 节的快速检查没有此示例或类似内容。 C++0x 是草稿标准,VC++/GCC/CLang 不兼容。

我的猜测:这是新的 auto 类型的一些副产品,在旧的 C++ 标准中找不到。

【讨论】:

    【解决方案4】:

    FWIW,我正在复制我给这个问题的 usenet 副本的答案:

    大家好,

    我对标准的 n3225 10.2/13 感到很困惑,
    [注:即使名称查找的结果是明确的,使用 在多个子对象中找到的名称可能仍然不明确(4.11, 5.2.5, 5.3.1, 11.2).—尾注] [示例:

    struct B1 {
      void f();
      static void f(int);
      int i;
    };
    struct B2 {
      void f(double);
    };
    struct I1: B1 { };
    struct I2: B1 { };
    struct D: I1, I2, B2 {
      using B1::f;
      using B2::f;
      void g() {
        f(); // Ambiguous conversion of this
        f(0); // Unambiguous (static)
        f(0.0); // Unambiguous (only one B2)
        int B1::* mpB1 = &D::i; // Unambiguous
        int D::* mpD = &D::i; // Ambiguous conversion
      }
    };
    

    我不明白为什么这是明确的 int B1::* mpB1 = &D::i; // 明确的

    &D::i 的类型为int B1::*,并且明确引用了数据成员i B1。如果您使用D 对象取消引用它,或者将其分配给int  D::*,您将根据需要获得歧义。

    Visual C++、Gcc 和 CLang 都说它是对 D::i 的模棱两可的访问!

    这些编译器都没有实现 10.2。

    措辞似乎与核心问题 #39 有关 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#39,和 最终提案在这里: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1626.pdf 我现在发现新的基于算法的措辞(10.2/3-10.2/6)是 更令人困惑的是,因为 10.2/9、10.2/10、 10.2/11 和 10.2/13 完全符合 10.2/3-10.2/6。我可以做 10.2/9-10.2/11 作为例外,但我特别困惑 10.2/13。我不知道 10.2/13 的意图。

    您需要举例说明您不理解的内容。

    10.2/13中的例子应该如何根据 10.2/3-10.2/6? 10.2/13 的意图是什么,即,什么是 10.2/13 被视为例外的情况 10.2/3-10.2/6?

    基于新算法的查找规则将运行时问题解耦(查找 唯一对象)来自编译时/查找问题(找到一个声明 一个名字是指)。

    以下内容使用新的措辞格式正确:

    struct Z { int z; };
    struct X : Z { };
    struct Y : Z { };
    struct A : X, Y { };
    
    struct B : A {
      using A::z;
    };
    

    声明using A::x; 在 B 中引入了一个成员名称,该成员名称指向 声明Z::z。在声明性上下文中,这非常好。一个 仅当您将 B::z 作为成员访问表达式访问时才会出现错误 (5.2.5)。

    不要因为把成员指针的情况弄错了而感到难过。 I did so too 过去,对应的issue report 居然把它做成了 C++0x 草案。他们幸运地changed it back 当他们注意到更改是错误的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-21
      • 2015-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      相关资源
      最近更新 更多