【问题标题】:Strange C++ rule for member function pointers? [duplicate]成员函数指针的奇怪 C++ 规则? [复制]
【发布时间】:2011-10-31 08:18:59
【问题描述】:

可能重复:
Error with address of parenthesized member function

this recent question 中,OP 遇到了 C++ 语言的一个奇怪规定,如果该成员函数名称带有括号,则获取该成员函数的地址是非法的。例如,这段代码是非法的:

struct X {
    void foo();
};

int main() {
    void (X::* ptr)();
    ptr = &(X::foo);   // Illegal; must be &X::foo
}

我查了一下,发现这是由于 C++ ISO 规范的 §5.3.1/3 造成的,它显示

仅当使用显式 & 且其操作数是未括在括号中的限定 ID [...]

时才会形成指向成员的指针

有人知道为什么规范有这个规则吗?它特定于指向成员的指针,所以我怀疑这解决了一些语法歧义,但老实说,我一点也不知道它可能是什么。

【问题讨论】:

  • @Hans:不是重复的。 OP 实际上是该问题的评分最高的答案,它询问 什么 问题是什么。这个问题是问为什么
  • @Troubadour:某事的 WHY 与 WHAT 和 HOW 不同。例如,what-question“唐老鸭穿什么样的裤子?”答案是“他下半身赤身裸体”,但如果你问“为什么唐老鸭下身赤身裸体”的问题,那就是另一个答案,即“他是一只鸭子”。简而言之,如果您问“唐老鸭穿什么样的裤子”,那么说“他是鸭子”是不是的有效答案。如您所见,2 diff q 与 2 diff a。干杯,
  • @Troubador- 我不打算让这成为您问题的重复。我对您的问题的解释是“这个问题的根本原因是什么”,而不是“为什么 C++ 采用这种结构”,所以我问这个问题是为了得到后一个问题的答案。如果我误读了您最初的问题并在此重复,我深表歉意。
  • 原来的问题为什么。 templatetypedef 似乎将其误解为“什么”,这很愚蠢,因为最初的问题是“在获取非静态成员函数的地址时不允许使用括号的原因是什么?”这简直是​​个骗局。
  • @templatetypedef:这不是 Troubador 的问题。游吟诗人没有问题。

标签: c++ language-lawyer pointer-to-member


【解决方案1】:

这只是个人意见。 如果&(qualified-id) 被允许为&(unary-expression), qualified-id 必须是一个表达式,并且一个表达式应该有一个类型 (即使它不完整)。 但是,C++ 没有表示成员的类型,只有 指向成员的指针。 例如下面的代码无法编译。

struct A { int i; };

template< class T > void f( T* );

int main() {
  (void) typeid( A::i );
  f( &A::i );
}

为了使&amp;(qualified-id) 有效,编译器必须持有 内部的成员类型。 但是,如果我们放弃 &amp;(qualified-id) 符号,编译器就不需要 处理成员类型。 由于成员类型总是以指向它的指针的形式处理, 我猜标准优先简化编译器的类型 系统一点。

【讨论】:

  • +1 我认为你是对的。 :-)
  • @AlfP.Steinbach:哇!这真是令人鼓舞:-D
  • 太棒了!这似乎是完全正确的答案。非常感谢你发布这个!
  • @templatetypedef:哦,一点也不!很高兴答案有帮助:-)
  • 请记住,您可以在 C++11 中说 sizeof A::isizeof(A::i + 42)。您可以使用括号包裹名称来调用非静态函数:struct A { void f() { } void g() { (f)(); } };f 的类型在此处为 void())。
【解决方案2】:

想象一下这段代码:

struct B { int data; };
struct C { int data; };

struct A : B, C {
  void f() {
    // error: converting "int B::*" to "int*" ?
    int *bData = &B::data;

    // OK: a normal pointer
    int *bData = &(B::data);
  }
};

如果没有括号的技巧,您将无法将指针直接指向 B 的数据成员(您将需要基类强制转换和带有 this 的游戏 - 不好)。


来自 ARM:

请注意,必须显式使用地址运算符来获取指向成员的指针;没有隐式转换……如果有,我们在成员函数的上下文中就会有歧义……例如,

void B::f() {
    int B::* p = &B::i; // OK
    p = B::i; // error: B::i is an int
    p = &i; // error: '&i'means '&this->i' which is an 'int*'

    int *q = &i; // OK
    q = B::i; // error: 'B::i is an int
    q = &B::i; // error: '&B::i' is an 'int B::*'
}

IS 只是保留了这个 pre-Standard 概念,并明确提到括号使它不会得到指向成员的指针。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 2014-07-26
    • 2016-11-21
    相关资源
    最近更新 更多