2017-06-24 9 views
0

私はcerealboost::serializationコードを見て、クラス登録の仕組みを理解していますが、瞬時には理解できません。これはどのように機能しますか?

  • を私はstatic_object<magic<B№>>の明示的なインスタンス化を要求:

    はここで何が起こるか私の理解です。

  • コンストラクタ内にadl_magicが存在するため、コンパイラはすべてのオーバーロードをインスタンス化します。
  • これらのオーバーロードの一部の戻り値の型はtypename instantiator<inserter<A№, T>>::typeを参照するため、コンパイラはインスタンスinstantiator(意図しない句)を作成します。
  • 今、私は次に何が起こるのか分かりません。 static_object<nserter<A№, T>>は、決して呼び出されない関数の内部から参照されていますが、なぜインスタンス化されますか?そして、なぜdummyが必要なのですか(なぜ異なるコンパイラが別のものを必要とするのですか)? typename instantiator<inserter<A№, T>>::typetypename static_object<inserter<A№, T>>::typeに置き換えても、なぜ機能しないのですか?

残りのコードはかなりわかりやすいようです。

template<typename T> 
class static_object { 
    static void use(T const&) {} 

    static T& ref; 
    static T& create() 
    { 
     static T object; 
     use(ref); // why it doesn't work without this line? 
     return object; 
    } 

public: 
    static T& instance() { return create(); } 
}; 
template <class T> 
T& static_object<T>::ref = static_object<T>::create(); 



template <void(*)()> struct instantiate_function {}; 

template<typename T> 
struct instantiator { 
    static void instantiate() { static_object<T>::instance(); } 

#ifdef _MSC_VER 
    virtual void dummy() { instantiate(); } 
#else 
    using dummy = instantiate_function<instantiate>; 
#endif 
}; 

#include <string> 
#include <vector> 
// This gets called when stuff below is instantiated 
using list = std::pair<std::string, std::string>; 
using list = static_object<std::vector<string_pair>>; 
template<typename A, typename B> 
struct inserter { 
    inserter() 
    { 
     list::instance().push_back(std::pair{A::name, B::name}); 
    } 
}; 

// These are just some structs for demonstration. 
struct A1 { static const char name[]; }; const char A1::name[] = "A1"; 
struct A2 { static const char name[]; }; const char A2::name[] = "A2"; 
struct B1 { static const char name[]; }; const char B1::name[] = "B1"; 
struct B2 { static const char name[]; }; const char B2::name[] = "B2"; 
struct B3 { static const char name[]; }; const char B3::name[] = "B3"; 

// I've omitted an "adl_tag" argument, which is needed to make 
// sure ADL finds all overloads 
template<typename T> void adl_magic(T*, int) {} 

// each of these would be behind some REGISTER_ARCHIVE(A) macro 
template<typename T> typename instantiator<inserter<A1, T>>::type adl_magic(T*, A1*); 
template<typename T> typename instantiator<inserter<A2, T>>::type adl_magic(T*, A2*); 

template<typename T> 
struct magic { 
    magic() 
    { 
     adl_magic(static_cast<T*>(nullptr), 0); 
    } 
}; 

// each of these would be behind some REGISTER_CLASS(B) macro 
template struct static_object<magic<B1>>; 
template struct static_object<magic<B2>>; 
template struct static_object<magic<B3>>; 

#include <iostream> 
int main() 
{ 
    for(auto& p : list::instance()) 
     std::cout << p.first << ' ' << p.second << '\n'; 
} 

編集:私はこれらと上記対応する宣言を変更した場合、彼らはすべてのコンパイラで動作するように見えます。私はなぜ彼らが働くのかわかりませんが、autostatic_object<T>のインスタンス化を強制してタイプを推測するので、そうすると思います。唯一のGCCおよびない他のコンパイラで動作する別のバリ​​エーションがあり、

template<typename T> 
struct instantiator { 
    static auto instantiate() { return static_object<T>::instance(); } 
}; 

template<typename T> decltype(instantiator<inserter<A1, T>>::instantiate()) adl_magic(T*, A1*); 
template<typename T> decltype(instantiator<inserter<A2, T>>::instantiate()) adl_magic(T*, A2*); 

:今

template<typename T> 
struct instantiator { 
    static T& ref; 
}; 
template<typename T> 
T& instantiator<T>::ref = static_object<T>::instance(); 

template<typename T> decltype(instantiator<inserter<A1, T>>::ref) adl_magic(T*, A1*); 
template<typename T> decltype(instantiator<inserter<A2, T>>::ref) adl_magic(T*, A2*); 
+0

8つの疑問符があるように見えるので、あなたの質問は非常に難しいようです。小さなコンパイル可能なサンプルを作成し、最小限の疑問符を付けることをお勧めします。 –

+0

すべてのコードブロックを1つにまとめると、コンパイル可能な最小の例です。 – Hedede

+0

@Johannes Schaub - litb、私は1つのブロックにコードを結合し、疑問符の量を減らしました。私は今、それを解析するのがより簡単で(そして難しくない)ことを願っています。 – Hedede

答えて

0

が、私は次に何が起こるかわかりません。 static_objectが呼び出されない関数の内部から参照されているにもかかわらず、static_objectをインスタンス化するのはなぜですか?

::typeはそのクラステンプレートの特殊化を調べる必要があるため、インスタンス化する必要があるためインスタンス化が行われます。

なぜダミーが必要なのですか(なぜ異なるコンパイラで異なるものが必要なのですか)?他に何もしません一方

は、括弧で囲まれた部分に答えるが、dummy参照instantiateすることはできません。コンパイラがその中のコードをコンパイルするように、何かがinstantiateを参照しなければならない。クラステンプレートの仮想メンバ関数は、未使用の場合でも常にインスタンス化されるため、そのトリックが実行されます。 usingエイリアスは、ファンクションのアドレスを別のテンプレートに渡し、コンパイラがinstantiateをコンパイルするようトリガします。

なぜtypename instantiator> :: typeをtypename static_object> :: typeに置き換えても機能しないのですか?

ので、あなたはstatic_object<T>をインスタンス化している、しかし、それは唯一の(仮想メンバ関数または使用して別名など)メンバ関数の宣言と静的データメンバの宣言と、このようなインスタンスを作成します。メンバ関数本体ではありません。したがって、静的データメンバの定義static_object<...>のインスタンス化をトリガしないため、オブジェクトはTを作成しないため、listは拡張されません。 useに渡すことでstatic_object<T>::create参照... refを参照static_object<T>::instance

use(ref); // why it doesn't work without this line? 

instantiateので参照。その最後のものが削除された場合、何も存在しないため、その定義はインスタンス化されません。

関連する問題