2017-06-24 17 views
0

我一直在寻找cerealboost::serialization代码来了解类注册如何工作,但无法理解片刻。这是如何工作的? (C++名称查找魔术)

这里是我的理解发生了什么:

  • 我要求的static_object<magic<B№>>的显式实例。
  • 在构造函数中调用adl_magic,这会导致编译器实例化其所有重载。
  • 因为某些重载的返回类型指的是typename instantiator<inserter<A№, T>>::type,编译器会生成一个实例instantiator(没有双关语意思)。
  • 现在,我不明白接下来会发生什么。为什么它实例化static_object<nserter<A№, T>>,即使它是从一个永不被调用的函数内引用的?为什么需要dummy(以及为什么不同的编译器需要不同的编译器)?如果我用typename static_object<inserter<A№, T>>::type替换typename instantiator<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'; 
} 

编辑:如果我改变对应上面这些声明,它们似乎与所有的编译工作。我不明白他们为什么工作,但我认为他们这样做,因为auto强制实例化static_object<T>来推断类型。

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

这是最小的可编译示例,如果您将所有代码块组合为一个。 – Hedede

+0

@Johannes Schaub - litb,我将代码合并到一个块中,并减少了问题的数量。我希望现在解析起来更容易(而不是更难)。 – Hedede

回答

0

现在,我不知道接下来会发生什么。为什么它实例化static_object>,即使它是从一个从不被调用的函数内引用的?

实例化发生是因为::type需要查看该类模板专业化,所以它必须实例化它。

为什么需要哑元(以及为什么不同的编译器需要不同的编译器)?

无法回答加括号的部分,但dummy参考instantiate,而没有别的。必须引用instantiate,以便编译器编译其中的代码。一个类模板的虚拟成员函数总是被实例化,即使没有被使用,这样做的伎俩。 using别名将该函数的地址传递给另一个模板,该模板会触发编译器编译instantiate

如果我用typename static_object> :: type替换typename instantiator> :: type,它为什么不起作用?

因为那么你已经实例化static_object<T>,但仅实例化的成员函数的声明(如虚拟成员函数或使用别名)和静态数据成员声明和这样。不是成员函数体。因此,它不会触发静态数据成员的实例化定义static_object<...>,因此它不会创建T的对象,因此它不会扩展list

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

因为instantiate引用static_object<T>::instance引用static_object<T>::create引用... ref通过它传递给use。如果删除了最后一件事,则不再需要ref的存在,所以它的定义将不会被实例化。