Hello!
Решил разобраться, как работает в boost сериализация для полиморфных классов.
В общем-то вся соль сводится к такому коду:
#include <iostream>
#include <typeinfo>
using std::cout;
using std::endl;
//:singleton
template<class Q, class T>
class S
{
S()
{
cout << "S<" << typeid(Q).name() << ", " << typeid(T).name() << ">" << endl;
}
static S& instance_;
static void instantiate(S&)
{}
public:
static S& get_instance()
{
static S<Q, T> s;
instantiate(instance_);
return s;
}
void dummy()
{}
};
template<class Q, class T>
S<Q,T>& S<Q,T>::instance_ = S<Q,T>::get_instance();
template <void(*)()>
struct instantiate_function
{};
template <class Q, class T>
struct U
{
static void instantiate()
{
S<Q, T>::get_instance().dummy();
}
typedef instantiate_function<&U::instantiate> x;
};
template<typename T>
void F(T*, int)
{
cout << "F(0,0)" << endl;
}
//:archive visitor
class V
{
};
//:like BOOST_SERIALIZATION_REGISTER_ARCHIVE
template<typename T>
typename U<V, T>::x
F(T*, V*);
//:serializable
class A
{};
class B
{};
int main(int argc, char** argv)
{
F((A*)0, 0);
F((B*)0, 0);
return 0;
}
Вопрос: почему вызов F((A*)0, 0);
заставляет сгенериться класс S<V,A>?
Хотя функция
template<typename T>
typename U<V, T>::x
F(T*, V*);
даже не вызывается и в для overload хуже чем
template<typename T>
void F(T*, int)
(которая вызывается).
---
WBR
KlugXP:
KXP>Вопрос: почему вызов F((A*)0, 0);
KXP>заставляет сгенериться класс S<V,A>?
KXP>Хотя функция
KXP>template<typename T>
typename U<V, T>::x
F(T*, V*);
KXP>даже не вызывается и в для overload хуже чем
KXP>template<typename T>
void F(T*, int)
KXP>(которая вызывается).
Разрешение перегрузки происходит после определения множества функций-кандидатов. В данном случае первоначальное множество кандидатов состоит из двух шаблонов функций, из которых, согласно 13.3.1/7, должна быть предпринята попытка сгенерировать специализации путём выведения шаблонных аргументов и их подстановки на место шаблонных параметров. То, что получится в итоге, и будет множеством функций-кандидатов, участвующих в разрешении перегрузки.
Для обоих шаблонов T выводится в A. В итоге получаются специализации объявлений (неявно инстанцированные — см. 14.7.1/8):
void F(A *, int); // [T = A]
U<V, A>::x F(A *, V *); // [T = A]
Конструкция U<V, A>::x требует существование определения для U<V, A>. Насколько я понимаю, по текущему стандарту, если бы определение U<V, A> отсутствовало, то программа была бы ill-formed (лишь в C++0x SFINAE действительно оправдывает своё название). Если определение присутствует, то компилятор в любом случае должен поинтересоваться, есть ли в U<V, A> член с именем x, и если есть, то является ли он типом. Если члена с именем x нет или если он есть, но это не тип, то дедукция типа считается неудачной, и, как следствие, специализация исключается из числа кандидатов (я откровенно не понимаю, зачем комитет по стандартизации C++ приплёл сюда дедукцию, а не просто указал на исключение функции из рассмотрения — как говорится, пути комитета неисповедимы). В данном случае тип x присутствует в U<V, A>, поэтому обе специализации включаются в множество функций-кандидатов. Дальнейшая судьба этих специализаций в ходе разрешения перегрузки никак не влияет на тот факт, что компилятору пришлось-таки инстанцировать U<V, A>. Функция U<V, A>::instantiate используется в программе (см. 3.2/2), поэтому требуется наличие в программе её определения и эта функция инстанцируется (см. 14.7.1/2). Она, в свою очередь, инстанцирует S<V, A>.
Hello All!
>>Функция U<V, A>::instantiate используется в программе (см. 3.2/2), поэтому требуется наличие в программе её определения и эта функция инстанцируется (см. 14.7.1/2). Она, в свою очередь, инстанцирует S<V, A>.
Еще следует отметить необходимость использования трюка __declspec(dllexport) или __attribute((used)), чтобы некоторые шибко умные оптимизаторы (или линкеры) не выбросили S<V, A>::instance_:
BOOST_DLLEXPORT static S& instance_;
---
WBR