3

検討:

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 が失敗する理由を説明しています。ありがとう!

4

2 に答える 2

6

[あなたのプログラムのコードは次のようになっていると仮定しています: void (bar::*p)() = &bar::foobar;]

問題は、using 宣言が識別子をスペースに持ち込まないことではなく、 のセマンティクスです&bar::foobar。これで欠陥レポートを補充することを検討しています(時間があればやりたかったのですが)。すでにそのようなレポートがあります。

基本的に問題は、using 宣言によって基底関数が派生型のルックアップのスコープに持ち込まれ、式のアクセス指定子が&bar::foobarに対してチェックされることbarです。ただし、式の結果は&bar::foobarvoid (foo::*)()ではなく型void (bar::*)()です。ここで、 を評価した後、それをコンパイラ&bar::foobarとして使用しようとすると、ポインタのメンバーへの変換を実行しようとしますが、 が のベースであり、コンテキストではその関係にアクセスできないため失敗します。void (bar::*)()fooprotectedbarmain

これは 2 つの理由から言語の欠陥であると考えていることに注意してください。まず、コードが壊れます。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

関連リンク:


の使用に関するコメントの後、bindメンバーへのポインターを のメンバーへのポインターに直接変換しようとしている場合ではないかもしれませんが、コードbar内のどこかで、bindメンバーへのポインターを のインスタンスに適用するために生成されますbar。変換が必要です。

于 2012-08-22T13:44:31.423 に答える
3

注:これは元の質問に答えます。David Rodriguez が最新情報に回答しました。

あなたのコードは問題ありません。言語標準によると:

7.3.3/2 すべての using 宣言宣言メンバー宣言です

つまり、あなたはおよびfoobarのメンバーであることを宣言しているので、barとしてfoo言及することbar::foobarは と同じくらい正当foo::foobarです。

私のコンパイラ (GCC) はこれに同意します。そうでない場合は、バグがあるように見えます。

于 2012-08-22T13:31:02.813 に答える