7

次の点を考慮してください。

struct B { };

template<typename T>
struct D : B
{
    T t;
}

void g(int i) { ... }
void g(string s) { ... }
void g(char c) { ... }

void f(B* b)
{
    if (dynamic_cast<D<int>*>(b))
    {
        g(dynamic_cast<D<int>*>(b)->t);
    }
    else if (dynamic_cast<D<string>*>(b))
    {
        g(dynamic_cast<D<string>*>(b)->t);
    }
    else if (dynamic_cast<D<char>*>(b))
    {
        g(dynamic_cast<D<char>*>(c)->t)
    }
    else
        throw error;
};

ここでは、可能な T の型は int、string、char の 3 つしかありませんが、可能な型のリストがより長い場合、たとえば n の場合、if else チェーンの実行には O(n) が必要になります。

これに対処する 1 つの方法は、追加の型コードを何らかの方法で D に格納し、次に型コードに格納することswitchです。

RTTI システムには、そのようなコードがすでに存在している必要があります。それにアクセスしてスイッチを入れる方法はありますか?

または、私がやろうとしていることを行うためのより良い方法はありますか?

4

3 に答える 3

4

C++11 はもうすぐです。

caseC++03 では、(必要な) コンパイル時の定数を取得する唯一の方法が型システムを使用することだったため、これは不可能でした。typeidは常に同じ型を返すため、 に対して異なる代替を生成できませんでしたswitch

C++11 は型の一意の識別子としてconstexprandを追加しtype_info::hash_codeますが、それらを結合しません。typeid型名または静的に型指定された式の定数式で使用できますがhash_code、 はconstexpr関数ではないため、呼び出すことはできません。

もちろん、さまざまな回避策があります。そのうちの 1 つを説明し、その中で最も一般的なものは、テンプレート メタプログラミングを使用して型ベクトルにビジターを適用します。

于 2012-12-24T03:51:21.100 に答える
3

有効な型はごくわずかであるため、代わりに仮想関数とテンプレートの特殊化を使用してこれを解決できます。

struct B
{
    virtual void g() = 0;
}

template<typename T>
struct D : public B
{
    T t;
};

template<>
struct D<int> : public B
{
    int t;
    void g() { /* do something here */ }
};

template<>
struct D<std::string> : public B
{
    std::string t;
    void g() { /* do something here */ }
};

template<>
struct D<char> : public B
{
    char t;
    void g() { /* do something here */ }
};

void f(B* b)
{
    b->g();
}

代わりに間違った型を指定したり、実行時チェックを要求したりすると、これはコンパイル時に失敗します (C++ は非常に苦手です)。

于 2012-12-24T04:00:55.187 に答える
0

C++ で実行時に型を切り替えるための主な選択肢は、仮想関数です。

それは非常に簡単です:

#include <string>
#include <iostream>
using namespace std;

struct Base
{
    virtual void g() const = 0;
};

template< class Type > void g( Type const& );

template<> void g( int const& ) { cout << "int" << endl; }
template<> void g( string const& ) { cout << "string" << endl; }
template<> void g( char const& ) { cout << "char" << endl; }

template< class Type >
struct Derived: Base
{
    Type t;
    virtual void g() const override { ::g<Type>( t ); }
};

void f( Base& b ) { b.g(); }

int main()
{
    Derived<int>().g();
}

愚かな O( n )の代わりに O(1) も効率的です。さらに、動的 (実行時) 型チェックの代わりに静的 (コンパイル時) 型チェックを使用することで、面倒な量のテストを節約できます。これ以上何が言えますか?本当に、型コードや列挙型などは忘れてください。Bertrand Meyer が Eiffel で enum をサポートしないことを選択したことを思い出してください。これは、人々が型コードのために enum を悪用する傾向があるためです。仮想関数を使用してください。

ねえ、仮想関数!

それ以外の場合は、タイプで動的ディスパッチが必要な場合に、これらは非常に便利です。

したがって、そのためには仮想関数を使用することをお勧めします。:)


EDIT::g :実際のコードで起こりうるあいまいさを避けるためにテンプレート化されています。

于 2012-12-24T04:47:26.340 に答える