48

以下を考えると:

template<typename T>
class A
{
public:
    static const unsigned int ID = ?;
};

ID が T ごとに一意のコンパイル時 ID を生成するようにします。__COUNTER__ブースト PP ライブラリを検討しましたが、これまでのところ成功していません。どうすればこれを達成できますか?

編集: ID は、switch ステートメントのケースとして使用できる必要があります

Edit2: 静的メソッドまたはメンバーのアドレスに基づくすべての回答が正しくありません。これらは一意の ID を作成しますが、コンパイル時に解決されないため、switch ステートメントのケースとして使用できません。

4

17 に答える 17

11

これは、コンパイラが標準に準拠していると仮定して十分です (1 つの定義規則に関して)。

template<typename T>
class A
{
public:
    static char ID_storage;
    static const void * const ID;
};

template<typename T> char A<T>::ID_storage;
template<typename T> const void * const A<T>::ID= &A<T>::ID_storage;

C++ 標準 3.2.5 から 1 つの定義規則 [basic.def.odr] (太字強調鉱山):

... D がテンプレートであり、複数の翻訳単位で定義されている場合、上記のリストの最後の 4 つの要件は、テンプレート定義 (14.6.3) で使用されるテンプレートの囲みスコープからの名前に適用され、さらにインスタンス化の時点での依存名 (14.6.2)。D の定義がこれらの要件をすべて満たす場合、プログラムは、D の定義が 1 つしかないかのように動作します。Dの定義がこれらの要件を満たさない場合、動作は未定義です。

于 2011-09-26T23:37:58.497 に答える
9

私が通常使用するのはこれです:

template<typename>
void type_id(){}

using type_id_t = void(*)();

関数のすべてのインスタンス化には独自のアドレスがあるため、そのアドレスを使用して型を識別できます。

// Work at compile time
constexpr type_id_t int_id = type_id<int>;

// Work at runtime too
std::map<type_id_t, std::any> types;

types[type_id<int>] = 4;
types[type_id<std::string>] = "values"s

// Find values
auto it = types.find(type_id<int>);

if (it != types.end()) {
    // Found it!
}
于 2016-09-22T13:51:01.450 に答える
4

これは私にとってはうまくいくようです:

template<typename T>
class Counted
{
  public:
  static int id()
  {
    static int v;
    return (int)&v;
  }
};

#include <iostream>

int main()
{
  std::cout<<"Counted<int>::id()="<<Counted<int>::id()<<std::endl;
  std::cout<<"Counted<char>::id()="<<Counted<char>::id()<<std::endl;

}
于 2011-09-27T00:10:17.977 に答える
4

この回答のコードを使用して、文字列からコンパイル時の HASH を生成することができます。

テンプレートを変更して追加の整数を 1 つ含め、マクロを使用して変数を宣言できる場合:

template<typename T, int ID> struct A
{
    static const int id = ID;
};

#define DECLARE_A(x) A<x, COMPILE_TIME_CRC32_STR(#x)>

型宣言にこのマクロを使用すると、id メンバーには型名のハッシュが含まれます。例えば:

int main() 
{
    DECLARE_A(int) a;
    DECLARE_A(double) b;
    DECLARE_A(float) c;
    switch(a.id)
    {
    case DECLARE_A(int)::id:
        cout << "int" << endl;
        break;
    case DECLARE_A(double)::id:
        cout << "double" << endl;
        break;
    case DECLARE_A(float)::id:
        cout << "float" << endl;
        break;
    };
    return 0;
}

タイプ名は文字列に変換されるため、タイプ名のテキストを変更すると、異なる ID になります。例えば:

static_assert(DECLARE_A(size_t)::id != DECLARE_A(std::size_t)::id, "");

もう 1 つの欠点は、ハッシュの衝突が発生する可能性があることです。

于 2016-09-16T23:57:15.863 に答える
3

この定数式カウンターを使用すると、次のようになります。

template <class T>
class A
{
public:
    static constexpr int ID() { return next(); }
};
class DUMMY { };
int main() {
    std::cout << A<char>::ID() << std::endl;
    std::cout << A<int>::ID() << std::endl;
    std::cout << A<BETA>::ID() << std::endl;
    std::cout << A<BETA>::ID() << std::endl;
    return 0;
}

出力: (GCC、C++14)

1
2
3
3

欠点は、定数式カウンターが機能するために、派生クラスの数の上限を推測する必要があることです。

于 2016-11-30T05:08:19.633 に答える
3

静的関数のメモリ アドレスを使用します。

template<typename T>
class A  {
public:
    static void ID() {}
}; 

(&(A<int>::ID))などとは異なり(&(A<char>::ID))ます。

于 2011-09-26T22:37:18.667 に答える
3

最近、この正確な問題に遭遇しました。私の解決策:

カウンター.hpp

class counter
{
    static int i;
    static nexti()
    {
        return i++;
    }
};

Counter.cpp:

int counter::i = 0;

テンプレートクラス.hpp

#include "counter.hpp"

    template <class T>
    tclass
    {
        static const int id;
    };

    template <class T>
    int tclass<T>::id = counter::nexti();

MSVC と GCC で適切に動作するように見えますが、switch ステートメントでは使用できないという 1 つの例外があります。

さまざまな理由から、実際にはさらに先に進み、共通ベースから派生した静的 ID (上記のように) を持つ特定の名前パラメーターから新しいクラスを作成するプリプロセッサー マクロを定義しました。

于 2011-09-27T12:45:03.183 に答える
2

主にテンプレートに基づく可能な解決策を次に示します。

#include<cstddef>
#include<functional>
#include<iostream>

template<typename T>
struct wrapper {
    using type = T;
    constexpr wrapper(std::size_t N): N{N} {}
    const std::size_t N;
};

template<typename... T>
struct identifier: wrapper<T>... {
    template<std::size_t... I>
    constexpr identifier(std::index_sequence<I...>): wrapper<T>{I}... {}

    template<typename U>
    constexpr std::size_t get() const { return wrapper<U>::N; }
};

template<typename... T>
constexpr identifier<T...> ID = identifier<T...>{std::make_index_sequence<sizeof...(T)>{}};

// ---

struct A {};
struct B {};

constexpr auto id = ID<A, B>;

int main() {
    switch(id.get<B>()) {
    case id.get<A>():
        std::cout << "A" << std::endl;
        break;
    case id.get<B>():
        std::cout << "B" << std::endl;
        break;
    }
}

これには C++14 が必要であることに注意してください。

シーケンシャル ID をタイプのリストに関連付けるために必要なことは、上記の例のように、そのリストをテンプレート変数に提供することだけです。

constexpr auto id = ID<A, B>;

getその時点から、メソッドを使用して、指定されたタイプの指定された ID を取得できます。

id.get<A>()

それだけです。switch要求に応じて、コード例に示されているように、ステートメントで使用できます。

数値 ID を関連付けるクラスのリストに型が追加されている限り、各コンパイル後および各実行中の識別子は同じであることに注意してください。
リストからタイプを削除したい場合でも、例として偽のタイプをプレースホルダーとして使用できます。

template<typename> struct noLonger { };
constexpr auto id = ID<noLonger<A>, B>;

これによりA、 に関連付けられた ID がなくなり、 に指定された IDBが変更されなくなります。
絶対に削除したくない場合はA、次のようなものを使用できます。

constexpr auto id = ID<noLonger<void>, B>;

または何でも。

于 2016-09-22T20:39:26.900 に答える
2

わかりました.....これは、このWebサイトから見つけたハックです。それはうまくいくはずです。structあなたがする必要がある唯一のことは、カウンター「メタオブジェクト」を取る別のテンプレートパラメーターをあなたに追加することです。Awith intboolおよびcharすべてに一意の ID があることに注意int1てください。 bool2

別の注意:

これは、Microsoft Visual C++ では機能しません。

#include <iostream>
#include "meta_counter.hpp"

template<typename T, typename counter>
struct A
{
    static const size_t ID = counter::next();
};

int main () {
    typedef atch::meta_counter<void> counter;
    typedef A<int,counter> AInt;
    typedef A<char,counter> AChar;
    typedef A<bool,counter> ABool;
    switch (ABool::ID)
    {
        case AInt::ID:
            std::cout << "Int\n";
            break;
        case ABool::ID:
            std::cout << "Bool\n";
            break;
        case AChar::ID:
            std::cout << "Char\n";
            break;
    }

    std::cout << AInt::ID << std::endl;
    std::cout << AChar::ID << std::endl;
    std::cout << ABool::ID << std::endl;
    std::cout << AInt::ID << std::endl;
    while (1) {}
}

ここにあるmeta_counter.hpp

// author: Filip Roséen <filip.roseen@gmail.com>
// source: http://b.atch.se/posts/constexpr-meta-container

#ifndef ATCH_META_COUNTER_HPP
#define ATCH_META_COUNTER_HPP

#include <cstddef>

namespace atch { namespace {

  template<class Tag>
  struct meta_counter {
    using size_type = std::size_t;

    template<size_type N>
    struct ident {
      friend constexpr size_type adl_lookup (ident<N>);
      static constexpr size_type value = N;
    };

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    template<class Ident>
    struct writer {
      friend constexpr size_type adl_lookup (Ident) {
        return Ident::value;
      }

      static constexpr size_type value = Ident::value;
    };

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    template<size_type N, int = adl_lookup (ident<N> {})>
    static constexpr size_type value_reader (int, ident<N>) {
      return N;
    }

    template<size_type N>
    static constexpr size_type value_reader (float, ident<N>, size_type R = value_reader (0, ident<N-1> ())) {
      return R;
    }

    static constexpr size_type value_reader (float, ident<0>) {
      return 0;
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    template<size_type Max = 64>
    static constexpr size_type value (size_type R = value_reader (0, ident<Max> {})) {
      return R;
    }

    template<size_type N = 1, class H = meta_counter>
    static constexpr size_type next (size_type R = writer<ident<N + H::value ()>>::value) {
      return R;
    }
  };
}}

#endif /* include guard */
于 2016-09-23T22:42:11.063 に答える
1

これはできません。静的オブジェクトへのアドレスは、一意のIDに最も近いアドレスですが、そのようなオブジェクトのアドレス(静的定数積分であっても)を取得するには、どこかで定義する必要があります。1つの定義規則に従って、これらはCPPファイル内で定義する必要があります。これは、テンプレートであるため実行できません。ヘッダーファイル内で統計を定義すると、各コンパイルユニットは、もちろん異なるアドレスに実装された独自のバージョンを取得します。

于 2011-09-26T23:58:44.103 に答える
1

__DATE__および__TIME__マクロを使用して型の一意の識別子を取得する C++ コードを次に示します。<T>

フォーマット:

// __DATE__ "??? ?? ????"
// __TIME__ "??:??:??"

これは低品質のハッシュ関数です。

#define HASH_A 8416451
#define HASH_B 11368711
#define HASH_SEED 9796691    \
+ __DATE__[0x0] * 389        \
+ __DATE__[0x1] * 82421      \
+ __DATE__[0x2] * 1003141    \
+ __DATE__[0x4] * 1463339    \
+ __DATE__[0x5] * 2883371    \
+ __DATE__[0x7] * 4708387    \
+ __DATE__[0x8] * 4709213    \
+ __DATE__[0x9] * 6500209    \
+ __DATE__[0xA] * 6500231    \
+ __TIME__[0x0] * 7071997    \
+ __TIME__[0x1] * 10221293   \
+ __TIME__[0x3] * 10716197   \
+ __TIME__[0x4] * 10913537   \
+ __TIME__[0x6] * 14346811   \
+ __TIME__[0x7] * 15485863

unsigned HASH_STATE = HASH_SEED;
unsigned HASH() {
    return HASH_STATE = HASH_STATE * HASH_A % HASH_B;
}

ハッシュ関数の使用:

template <typename T>
class A
{
public:
    static const unsigned int ID;
};

template <>
const unsigned int A<float>::ID = HASH();

template <>
const unsigned int A<double>::ID = HASH();

template <>
const unsigned int A<int>::ID = HASH();

template <>
const unsigned int A<short>::ID = HASH();

#include <iostream>

int main() {
    std::cout << A<float>::ID << std::endl;
    std::cout << A<double>::ID << std::endl;
    std::cout << A<int>::ID << std::endl;
    std::cout << A<short>::ID << std::endl;
}
于 2016-09-22T12:06:55.973 に答える
0

単調でない値とintptr_tが許容される場合:

template<typename T>
struct TypeID
{
private:
    static char id_ref;
public:
    static const intptr_t ID;
};

template<typename T>
  char TypeID<T>::id_ref;
template<typename T>
  const intptr_t TypeID<T>::ID = (intptr_t)&TypeID<T>::id_ref;

int が必要な場合、または単調に増加する値が必要な場合は、静的コンストラクターを使用することが唯一の方法だと思います。

// put this in a namespace
extern int counter;

template<typename T>
class Counter {
private:
  Counter() {
    ID_val = counter++;
  }
  static Counter init;
  static int ID_val;
public:
  static const int &ID;
};

template<typename T>
  Counter<T> Counter<T>::init;
template<typename T>
  int Counter<T>::ID_val;
template<typename T>
  const int &Counter<T>::ID = Counter<T>::ID_val;

// in a non-header file somewhere
int counter;

共有ライブラリとアプリケーションの間でこれらの手法を共有している場合、これらの手法はどちらも安全ではないことに注意してください!

于 2011-09-26T23:53:36.193 に答える