6

次のコードでは、コンパイル時エラーが発生します。

' base::print': クラス ' base_der'で宣言されたプライベート メンバーにアクセスできません

ただし、public派生クラスでメンバーを作成しました。なぜこれが機能しないのですか?

#include <iostream>

using namespace std;

class base
{
public:
    int i;
    void print(int i)
    {
        printf("base i\n");
    }
};

class base_der : private base
{
public:
    using base::print;
};

int main()
{
    // This works:
    base_der cls;
    cls.print(10);

    // This doesn't:    
    void (base_der::* print)(int);
    print = &base_der::print; // Compile error here
}
4

3 に答える 3

5

エラーの原因となる相互作用する問題がいくつかあると思います。

  1. メンバーへのポインター型には、直観的でない型変換特性があります
  2. using 宣言は、スコープに持ち込まれる名前の型には影響しません
  3. 名前base_der::printにはアクセスできますが、クラスbaseはまだアクセスできず、ポインターからメンバーへの型を変換しようとすると、ポインターからメンバーへの型のクラスの実際の型が考慮されます。

C++03 7.3.3「using 宣言」

using 宣言は、using 宣言が現れる宣言領域に名前を導入します。その名前は、他の場所で宣言されているエンティティの名前の同義語です。

名前は新しい「地域」に持ち込まれますが、同義語であることに注意してください。名前が参照するもののタイプは同じです。したがって、あなたの例では、名前base_der::printには typevoid (base::*)(int)ではなく typeがあると思いますvoid (base_der::*)(int)

C++03 標準では、ポインターからメンバーへの型間の変換についても次のように述べられています ( 4.11 "ポインターからメンバーへの変換" )。

「cv T 型の B のメンバーへのポインター」型の右辺値 (B はクラス型) は、「cv T 型の D のメンバーへのポインター」型の右辺値 (D は派生クラス) に変換できます ( B の第 10 節)。 B が D のアクセス不能 (第 11 節)、あいまいな (10.2) または仮想 (10.1) 基本クラスである場合、この変換を必要とするプログラムは形式が正しくありません。変換の結果は、変換前のメンバーへのポインターと同じメンバーを参照しますが、基底クラスのメンバーを派生クラスのメンバーであるかのように参照します。結果は、B の D のインスタンスのメンバーを参照します。結果の型は「cv T 型の D のメンバーへのポインター」であるため、D オブジェクトで逆参照できます。結果は、B のメンバーへのポインターが D の B サブオブジェクトで逆参照された場合と同じになります。

7.3.3/13「The using宣言」にも注意してください(強調を追加):

オーバーロード解決のために、using 宣言によって派生クラスに導入された関数は、派生クラスのメンバーであるかのように扱われます。特に、暗黙の this パラメータは、基本クラスではなく、派生クラスへのポインタであるかのように扱われます。これは関数の型に影響を与えず、他のすべての点で、関数は基本クラスのメンバーのままです。

ここで、エラーを生成するコード例:

// This doesn't:    
void (base_der::* print)(int);
print = &base_der::print; // Compile error here

「D のメンバーへのポインター」を「B のメンバーへのポインター」に変換しようとしていますが、これは間違った方向への変換です。少し考えてみれば、なぜこの方向への変換が安全でないのかがわかるでしょう。「B のメンバーへのポインター」型の変数は、何か関係のあるオブジェクトでは使用できない可能性がありますがclass D、「D のメンバーへのポインター」型の関数を呼び出すと (これが何であるかvoid (base_der::* print)(int))、それはthisポインターがオブジェクトを指していることが当然期待できDます。

とにかく、問題の根本はこの変換の問題だと思いますが、コンパイラが変換を処理しようとしているときに、最初にアクセシビリティをチェックしているため、アクセシビリティについて苦情が寄せられていると思いますbasebase_der::print(これは のエイリアスですbase::print) はusing宣言のためにアクセスできますが、クラスbaseはまだアクセスできません。

免責事項: この分析は、ポインターからメンバーへの型のニュアンスについてほとんど経験のない人からのものです。それらは C++ の複雑な領域であり、最も単純なシナリオを除いて使用するのが難しく、明らかに多くの移植性の問題があります (Doug Clugston の記事http://www.codeproject.com/KB/cpp/FastDelegate. aspx、これらの問題の多くが今では対処されている可能性があるほど古いものですが、そうではないと思います)。

そして、C++ の何かがより複雑であまり理解されていない領域の 1 つであると言うとき、それは多くのことを語っています。

于 2011-08-04T18:03:59.887 に答える
4

理由を知っているとは言えませんが(仕様について話すこともできません)、clangのエラーメッセージは参考になるかもしれません:

error: cannot cast private base class 'base' to 'base_der'

したがって、少なくともclangとgccでは、メンバー関数のタイプを変更すると機能します。

void (base::* print)(int);
print = &base_der::print; // works!
于 2011-08-04T15:24:27.247 に答える
1

その理由は、

class base_der : private base

継承はprivateです。したがってbase、にアクセスできませんbase_der。これをに変更するpublicと、機能します。

于 2011-08-04T14:15:30.557 に答える