为什么使用声明不公开指向成员的指针

Why does using declaration not expose pointer to member

本文关键字:成员 指针 声明 为什么      更新时间:2023-10-16

考虑:

struct foo
{
    void foobar(){}
};
struct bar : protected foo
{
    using foo::foobar;
};
int main()
{
    bar b;
    b.foobar(); // Fine
    &bar::foobar; // Not fine
}

我想知道让使用声明公开成员,而不是指向它的指针的理由是什么。事实上,似乎所有更改访问级别的使用声明都适用于除获取公开函数地址之外的所有内容。

UPDATE:一个更像我真实用例的例子:

#include "boost/bind.hpp"
struct foo
{
    void foobar() {}
};
struct bar : protected foo
{
    using foo::foobar;
    bar() { boost::bind( &bar::foobar, this )(); } // Crashes VS2008, GCC 4.1.1 fails to compile as it tries to go through foo*
};
int main()
{
    bar b;
}

然而,Mike Seymours的解释很到位,解释了GCC失败的原因。谢谢

[我假设在您的程序中,代码是:void (bar::*p)() = &bar::foobar;]

问题不在于using声明没有将标识符带入空间,而在于&bar::foobar的语义。我正在考虑(如果我有时间的话,我会这么做的)用这个重新填充缺陷报告。已经有一份这样的报告。

基本上,问题是using声明将基函数带入派生类型中的查找范围,并且将对照bar检查表达式&bar::foobar的访问说明符但是,表达式&bar::foobar的结果是类型void (foo::*)(),而不是void (bar::*)()。现在,在评估&bar::foobar之后,如果您尝试将其用作void (bar::*)(),编译器将尝试执行指向成员的指针的转换,但会失败,因为foobarprotected基,并且在main的上下文中,您无法访问该关系。

请注意,我认为这是该语言的一个缺陷,原因有两个:首先,它破坏了您的代码:void (bar::*p)() = &bar::foobar;出人意料地无法编译。其次,它在其他情况下破坏了访问保护:

class base {
protected: void f() {}
};
struct derived : base {
   void foo( base& b ) {
       b.f();                // Error
       b.*(&derived::f)();   // OK
   }
};

这个问题实际上与您的问题是对称的,而在您的问题中,成员操作的地址这一令人惊讶的类型会在不应该使用的情况下抑制您的用例,在这种情况下,它允许违背protected意图的使用。

相关链接:

  • DR 203-成员表达式的地址类型
  • DR 1007-受保护的访问和指向成员的指针

在对使用bind发表评论后,您可能不会试图将指向成员的指针直接转换为指向bar成员的指针,但bind内部的某个地方会生成代码,将指向成员指针应用于bar的实例,这需要转换。

注意:这回答了最初的问题;David Rodriguez已经回答了最新消息。

你的代码很好。根据语言标准:

7.3.3/2每个使用声明都是声明员声明

这意味着您将foobar声明为barfoo的成员,因此将其称为bar::foobarfoo::foobar一样合法。

我的编译器(GCC)同意这一点;如果你的没有,那么它似乎有一个bug。