为什么使用声明不会将指针暴露给成员

时间:2012-08-22 13:16:55

标签: c++ visual-studio-2008

考虑:

struct foo
{
    void foobar(){}
};

struct bar : protected foo
{
    using foo::foobar;
};

int main()
{
    bar b;
    b.foobar(); // Fine
    &bar::foobar; // Not fine
}

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

更新:更好地类似于我的实际用例的示例:

#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失败的原因。谢谢!

2 个答案:

答案 0 :(得分:6)

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

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

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

请注意,我认为这是语言中的缺陷有两个原因:首先它会破坏您的代码:main令人惊讶地无法编译。其次,它在其他情况下打破了访问保护:

void (bar::*p)() = &bar::foobar;

这个问题实际上与你的问题是对称的,而在你的问题中,令人惊讶的成员地址操作类型会在不应该的情况下禁止你的用例,在这种情况下它会允许使用违反{{{ 1}}。

相关链接:


在使用class base { protected: void f() {} }; struct derived : base { void foo( base& b ) { b.f(); // Error b.*(&derived::f)(); // OK } }; 的注释之后,可能不是您尝试将指向成员的指针直接转换为指向protected成员的指针,而是在bind内的某处将生成代码以将指向成员的指针应用于bar的实例,这需要转换。

答案 1 :(得分:3)

注意:这回答了原始问题; David Rodriguez已回复此更新。

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

  

7.3.3 / 2每个 using-declaration 声明成员声明

这意味着您宣布foobar成为bar以及foo的成员,因此将其称为bar::foobar与{foo::foobar一样合法1}}。

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