2

背景:既存の Java クラス モデルに基づいて C++ コードを生成するフレームワークに取り組んでいます。このため、以下で説明する循環依存関係を変更することはできません。

与えられた:

  • 親子クラスの関係
  • 親には子のリストが含まれています
  • ユーザーは実行時にリスト要素のタイプを検索できる必要があります

これを次のテストケースでモデル化しました。

メイン.cpp

#include "Parent.h"

#include <iostream>
using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
    Parent parent;
    cout << Parent::getType() << endl;
    cout << parent.getChildren().getType() << endl;
    return 0;
}

Parent.h

#ifndef PARENT_H
#define PARENT_H

#include <string>

#include "Array.h"
class Child;

class Parent
{
public:
    Array<Child> getChildren()
    {
        return Array<Child>();
    }

    static std::string getType()
    {
        return "parent";
    }
};

#endif

Child.h

#ifndef CHILD_H
#define CHILD_H

#include "Parent.h"

class Child: public Parent
{
};

#endif

Array.h

template <typename ElementType>
class Array
{
public:
    static std::string getType()
    {
        return ElementType::getType();
    }
};
  1. 上記のコードをコンパイルすると、次のようになります error C2027: use of undefined type 'Child'return ElementType::getType();

  2. #include "Child.h"前方宣言の代わり に試してみると、次のようになりますerror C2504: 'Parent' : base class undefinedclass Child: public Parent

  3. Array<Child*>取得する代わりに試してみるとArray<Child>: error C2825: 'ElementType': must be a class or namespace when followed by '::'atreturn ElementType::getType();

循環依存は、次の理由で発生します。

  1. Child.h はクラス Parent について知る必要があります
  2. Parent.h はクラス Array について知る必要があります
  3. Array.h はクラス Child について知る必要があります

何か案は?

4

5 に答える 5

4

このエラーは、テンプレートのインスタンス化時に Child クラスが存在しないことが原因です。

以下を Main またはParent.hの最後に追加します。

#include "Child.h"

これは、g++ 4 と VS 2010 の両方で正常にコンパイルされます。

于 2010-07-05T08:38:47.340 に答える
4

この問題を解決する 1 つの方法は、実装をインターフェイスから分離することです。

したがって、Parent::getChildren() をコンパイルするときに、コンパイラが Parent、Child、および Array の定義を確認できるように、Parent の実装を .cpp ファイルに入れます。

#ifndef PARENT_H
#define PARENT_H
#include <string>
#include "Array.h"

class Child;

class Parent
{
public:
    Array<Child> getChildren();
    static std::string getType();
};

#endif

そしてparent.cppで:

#include "parent.hpp"
#include "child.hpp"

Array<Child> Parent::getChildren() {
    return Array<Child>();
}

// etc.

アップデート:

はい、実際の問題は Array::getType() が Child の定義なしでインスタンス化されることによって引き起こされるため、私の解決策は不完全です。

Pete Kirkham の解決策は良いです: main に child.hpp をインクルードするだけです。

インターフェイス/実装の分離が機能するためには、Array の明示的なインスタンス化とその他の必要なインスタンス化を使用して、Array の個別の実装ファイルが必要になります。これはおそらくあなたが望むものではありませんが、完全を期すために、次のようになります。

array.hpp で:

#ifndef ARRAY_HPP
#define ARRAY_HPP

#include <string>

template <typename ElementType>
class Array
{
public:
    static std::string getType();
};

#endif

そしてarray.cppで:

#include "array.hpp"
#include "child.hpp"

template<typename ElementType>
std::string Array<ElementType>::getType()
{
    return ElementType::getType();
}

template class Array<Child>;
于 2010-07-05T05:05:59.100 に答える
1

編集: OP は質問を編集して、私自身と rlbond が気づいた無限サイズのデータ​​構造の問題を削除しました。この変更により、janmの回答Array<Child>が示すように、 の代わりに使用できるようになりました。Array<Child*>

に変更Array<Child>Array<Child*>、型変更しArrayて、オブジェクト自体ではなくオブジェクトへのポインターが含まれていることを理解します。

新しい配列.h

// E.g. strip_pointer_from<Foo*>::type is Foo
template <typename T>
struct strip_pointer_from<T> {};

template <typename T>
struct strip_pointer_from<T*> {
    typedef T type;
};

template <typename ElementType>
class Array
{
public:
    static std::string getType()
    {
        return typename strip_pointer_from<ElementType>::type::getType();
    }
};

ただし、再考することを強くお勧めArrayします-プレーンvector<Child>を使用して、各要素にそのタイプを尋ねるだけの方法はありますか?

于 2010-07-05T05:07:21.110 に答える
0

以下は、メタクラス情報を必要とするシステムの問題を解決する方法です。問題のより直接的な解決策については、他の回答も参照してください。


stl で使用されるパターンは、文字列ではなく型を使用して型を表すことです。Sostd::vector<T>::value_typeは、ベクターに格納されている型を表します。この型を利用するかどうかは、クライアント コード次第です。

オブジェクトのランタイム型が必要な場合は、型を返す仮想関数を持つ基本クラスを使用します。場所の静的タイプの場合、部分的な特殊化を使用できます。

Object.h

#ifndef OBJECT_H
#define OBJECT_H

#include <string>

template <typename T>
struct type_info {
    // extend type_info<void*> to inherit defaults
    const static bool is_array = false;
};

template <typename T>
std::string type_name ( const T& )
{
    return type_info<T>::name();
};

template <typename T>
std::string type_name ()
{
    return type_info<T>::name();
};

#endif

Parent.h

#include "Object.h"
#include "Array.h"

class Child;
class Parent
{
public:
    Array<Child> getChildren() {
        return Array<Child>();
    }
};

template <>
struct type_info <Parent> : public type_info<void*> {
    static std::string name () {
        return "parent";
    }
};


#endif

Array.h

template <typename ElementType>
class Array
{
public:
    typedef ElementType value_type;
};

template <typename T>
struct type_info <Array<T > > {
    static std::string name () {
        return "Array<" + type_name<T>() + ">";
    }

    const static bool is_array = true;
};

Child.h

#ifndef CHILD_H
#define CHILD_H

#include "Parent.h"

class Child: public Parent
{
};

template <>
struct type_info <Child> : public type_info<void*> {
    static std::string name () {
        return "child";
    }
};

#endif

メイン.cpp

#include "Object.h"
#include "Parent.h"
#include "Child.h"

#include <iostream>
#include <iomanip>

using std::cout;
using std::endl;
using std::boolalpha;

template<typename T> bool type_is_array (const T&) { return type_info<T>::is_array; }

int main(int argc, char* argv[])
{
    Parent parent;
    cout << type_name<Parent>() << endl;
    cout << type_name(parent.getChildren()) << endl;
    cout << boolalpha << type_is_array(parent) << endl;
    cout << boolalpha << type_is_array(parent.getChildren()) << endl;
    return 0;
}
于 2010-07-05T08:24:52.397 に答える
0

代わりに、親の子へのポインタを使用できますか? 何かのようなもの

#ifndef PARENT_H
#define PARENT_H

#include <string>

#include "Array.h"
class Child;

class Parent
{
public:
    Array<Child*> getChildren()
    {
        return Array<Child*>();
    }

    static std::string getType()
    {
        return "parent";
    }
};

#endif

または、より一般的には、コンパイラのファイアウォール技術 (別名不透明ポインター、別名 PIMLP) を使用することが可能かもしれません。詳細については、こちらをご覧ください。

于 2010-07-05T05:06:46.540 に答える