вопрос о template instantiation
От: KlugXP Россия
Дата: 09.11.09 10:42
Оценка:
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
template instantiation
Re: вопрос о template instantiation
От: Николай Ивченков  
Дата: 09.11.09 15:42
Оценка: 2 (1)
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>.
Re[2]: вопрос о template instantiation
От: KlugXP Россия
Дата: 18.11.09 11:37
Оценка:
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
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.