web-dev-qa-db-ja.com

基本クラスメンバーデータへの派生テンプレートクラスアクセス

この質問は、 this thread で尋ねられた質問の助成です。

次のクラス定義を使用します。

template <class T>
class Foo {

public:
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
    {
        /* do something for foo */
    }
    T Foo_T;        // either a TypeA or a TypeB - TBD
    foo_arg_t _foo_arg;
};

template <class T>
class Bar : public Foo<T> {
public:
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
    : Foo<T>(bar_arg)   // base-class initializer
    {

        Foo<T>::Foo_T = T(a_arg);
    }

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
    : Foo<T>(bar_arg)
    {
        Foo<T>::Foo_T = T(b_arg);
    }

    void BarFunc ();

};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl;   // This doesn't work - compiler error is: error: ‘_foo_arg’ was not declared in this scope
    std::cout << Bar<T>::_foo_arg << std::endl;   // This works!
}

テンプレートクラスの基本クラスのメンバーにアクセスするとき、Bar<T>::_foo_argのテンプレートスタイルの構文を使用して、メンバーを常に明示的に修飾する必要があるようです。これを回避する方法はありますか?コードを簡素化するために、テンプレートクラスメソッドで「使用する」ステートメント/ディレクティブを使用できますか?

編集:

このスコープの問題は、変数をthis->構文で修飾することで解決されます。

56
Shamster

this->を使用して、クラスのメンバーを参照していることを明確にすることができます。

void Bar<T>::BarFunc () {
    std::cout << this->_foo_arg << std::endl;
}

または、メソッドで「using」を使用することもできます。

void Bar<T>::BarFunc () {
    using Bar<T>::_foo_arg;             // Might not work in g++, IIRC
    std::cout << _foo_arg << std::endl;
}

これにより、コンパイラは、メンバー名がテンプレートパラメータに依存していることを明確にし、適切な場所でその名前の定義を検索します。詳細については、 C++ Faq Liteのこのエントリ も参照してください。

61
sth

ここで、基本クラスは非依存基本クラスではありません(テンプレート引数を知らなくても決定できる完全な型を持つものを意味します)。また、_foo_argは非依存名です。標準C++では、非依存の名前は依存ベースクラスで検索されないという。

コードを修正するには、名前を_foo_argに依存させるだけで十分です。依存名はインスタンス化の時点でのみ検索でき、その時点で調査する必要のある正確なベースの専門化がわかるからです。例えば:

// solution#1
std::cout << this->_foo_arg << std::endl;

別の方法は、修飾名を使用して依存関係を導入することです。

// solution#2
std::cout << Foo<T>::_foo_arg << std::endl;

非修飾の非依存名を使用して仮想関数呼び出しを形成すると、修飾によって仮想呼び出しメカニズムが禁止され、プログラムの意味が変わるため、このソリューションには注意が必要です。

そして、usingにより、派生クラスの依存ベースクラスから名前を1回取得できます。

// solution#3
template <class T>
class Bar : public Foo<T> {
public:
    ...
    void BarFunc ();
private:
    using Foo<T>::_foo_arg;
};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl;   // works
}
23
songyuanyao

Visual C++ 2008で正常に動作するようです。あなたが言及したタイプのダミー定義をいくつか追加しましたが、ソースは提供していません。残りは、あなたが書いたとおりです。次に、BarFuncを強制的にインスタンス化して呼び出すメイン関数。

#include <iostream>

class streamable {};
std::ostream &operator<<(std::ostream &os, streamable &s) { return os; }

class foo_arg_t : public streamable {};
class a_arg_t : public streamable {};
class b_arg_t : public streamable  {};

template <class T>
class Foo {

public:
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
    {
        /* do something for foo */
    }
    T Foo_T;        // either a TypeA or a TypeB - TBD
    foo_arg_t _foo_arg;
};

template <class T>
class Bar : public Foo<T> {
public:
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
    : Foo<T>(bar_arg)   // base-class initializer
    {

        Foo<T>::Foo_T = T(a_arg);
    }

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
    : Foo<T>(bar_arg)
    {
        Foo<T>::Foo_T = T(b_arg);
    }

    void BarFunc ();

};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl; 
    std::cout << Bar<T>::_foo_arg << std::endl;   
}

int main()
{
    Bar<a_arg_t> *b = new Bar<a_arg_t>(foo_arg_t(), a_arg_t());
    b->BarFunc();
}
1