31

C++ で Struct または Class を反復処理してすべてのメンバーを見つけることは可能ですか? たとえば、構造体 a とクラス b がある場合:

struct a
{
  int a;
  int b;
  int c;
}

class b
{
  public:
    int a;
    int b;
  private:
    int c;
}

それらをループして、「構造体 a には a、b、c という名前の int があります」または「クラス b には a、b、c という名前の int があります」という print ステートメントを取得することは可能でしょうか?

4

6 に答える 6

32

これを行うにはいくつかの方法がありますが、いくつかのマクロを使用して構造体を定義または適合させる必要があります。

この回答REFLECTABLEで指定されたマクロを使用して、次のように構造体を定義できます。

struct A
{
    REFLECTABLE
    (
        (int) a,
        (int) b,
        (int) c
    )
};

そして、フィールドを反復処理して、次のように各値を出力できます。

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

A x;
print_fields(x);

もう 1 つの方法は、構造体を融合シーケンスとして適合させることです (ドキュメントを参照してください)。次に例を示します。

struct A
{
    int a;
    int b;
    int c;
};

BOOST_FUSION_ADAPT_STRUCT
(
    A,
    (int, a)
    (int, b)
    (int, c)
)

次に、これを使用してフィールドも印刷できます。

struct print_visitor
{
    template<class Index, class C>
    void operator()(Index, C & c)
    {

        std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call() 
                  << "=" 
                  << boost:::fusion::at<Index>(c) 
                  << std::endl;
    }
};


template<class C>
void print_fields(C & c)
{
    typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
    boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), boost::ref(c), _1));
}
于 2013-09-28T14:59:02.867 に答える
3

メンバー変数が同じ型であれば、GLM ライブラリから盗んだ次のようなことができます。

class Point
{
    Point();// you must re-implement the default constructor if you need one

    union
    {
        struct
        {
            double x;
            double y;
            double z;
        };
        std::array<double, 3> components;
    };
};

確かに、これは保守性の観点からは最も洗練されたソリューションではありません。手動で変数の数を数え続けるのは面倒です。ただし、ライブラリやマクロを追加しなくても機能し、この動作が必要なほとんどの状況に適用できます。

ユニオンは自動的に生成されたデフォルト コンストラクタをサポートしていないため、ユニオンを初期化する方法をオブジェクトに指示するコンストラクタを作成する必要があります。

for (double component : point.components)
{
    // do something
}
于 2019-07-04T12:01:11.003 に答える
1

すべてのクラス メンバーが同じ型であると仮定すると、構造化バインディングと呼ばれる C++17 機能を使用できます。すべてのメンバーがパブリックであると仮定すると、これは機能します。

struct SI
{
    int x;
    int y;
    int z;
};

struct SD
{
    double x;
    double y;
    double z;
};

template<typename T>
void print(const T &val)
{
    const auto& [a, b, c] = val;
    for (auto elem : {a, b, c})
    {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

これは、同じ (コピー可能な) 型のパブリック要素を 3 つだけ持つ構造体で機能します。非パブリック メンバーの場合、関数はフレンドまたはメンバーである必要があります。ただし、このアプローチは、任意の数の要素に簡単に拡張することはできません。

于 2020-06-11T16:37:18.810 に答える
1

これは QCTDevs の回答の改良版です。

class MyClass
{
    union
    {
        struct Memberstruct
        {
            double test0;
            double test1;
            double test2;
        } m;
        array<double, sizeof( Memberstruct ) / sizeof( double )> memberarray;
    };
    bool test() { &(m.test1) == &(memberarray[1]); }
};

要件は依然としてすべて同じデータ型を持つことであり、必要に応じてデフォルトのコンストラクターも実装する必要があります。

配列のサイズを手動で維持する必要がないという点で改善されています。

欠点は、この回避策がないクラスと比較して構文が変更されていることです。

于 2021-02-26T14:52:39.640 に答える