Внешние шаблоны C++11: где они на самом деле нужны?
В C++03 у нас есть явные определения экземпляра шаблона (template class Foo<int>
) которые вызывают создание экземпляра класса шаблона.
В C++11 у нас есть явные объявления экземпляра шаблона (extern template class Foo<int>
), который должен предотвратить неявные реализации класса шаблона. ( Создание шаблона класса)
Я пытаюсь смоделировать ситуацию, когда мне действительно нужно явное объявление экземпляра, чтобы уменьшить время компиляции. Но я не могу. Похоже, что все работает без этой функции (или не работает с ней).
Вот пример:
//Foo.h
#pragma once
template<class T>
class Foo
{
T inst;
public:
Foo(T i);
T& get() const;
};
//Foo.cpp
#include "stdafx.h"
#include "Foo.h"
template<class T>
Foo<T>::Foo(T inst) : inst(inst) { }
template<class T>
T& Foo<T>::get() const { return inst; }
template class Foo<int>; //explicit instantiation definition
//test1.h
#pragma once
#include "Foo.h"
//This line does not work
//extern template class Foo<int>; //explicit instantiation declaration.
void baz();
//test1.cpp
#include "stdafx.h"
#include "test1.h"
void baz()
{
Foo<int> foo(10);
int i = foo.get();
}
Результат не зависит от того, комментирую ли я (extern template class Foo<int>;
) линия или нет.
Вот символы обоих файлов *.obj:
dumpbin /SYMBOLS test1.obj
011 00000000 UNDEF notype () Внешний |?? 0? $ Foo @ H @@ QAE @ H @ Z (public: __thiscall Foo:: Foo (int)) '
012 00000000 UNDEF notype () Внешний |?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const)
013 00000000 SECT4 notype () Внешний |? baz @@YAXXZ (void __cdecl baz (void))
...
свалка / СИМВОЛЫ Foo.obj
017 00000000 SECT4 notype () Внешний |?? 0? $ Foo @ H @@ QAE @ H @ Z (public: __thiscall Foo:: Foo (int))
018 00000000 SECT6 notype () Внешний |?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const)
Обратите внимание, что Foo<int>::Foo<int>(int)
а также int Foo<int>::get(void)const
помечен как UNDEF в test1.obj, что означает, что они должны быть разрешены в другом месте (т.е. Foo был скомпилирован только ОДИН РАЗ).
ПОПЫТКА № 2:
Если я определю полный шаблон в файле Foo.h (без явного определения экземпляра), тогда extern template
не помогает - шаблон компилируется дважды (как в test1.cpp, так и в test2.cpp).
Пример:
//test1.h
#pragma once
#include "Foo.h"
void baz();
//test1.cpp
#include "stdafx.h"
#include "test1.h"
void baz()
{
Foo<int> foo(10); //implicit instantiation of Foo<int>
int i = foo.get();
}
//test2.h
#pragma once
#include "Foo.h"
extern template class Foo<int>;
void bar();
//test2.cpp
#include "stdafx.h"
#include "test2.h"
void bar()
{
Foo<int> foo(10); //should refer to Foo<int> from test1.obj but IT IS NOT
int i = foo.get();
}
Вот дампы символов:
свалка / СИМВОЛЫ test2.obj
01D 00000000 SECT4 notype () Внешний |?? 0? $ Foo @ H @@ QAE @ H @ Z (public: __thiscall Foo:: Foo (int))
01E 00000000 SECT8 notype () Внешний |?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const)
01F 00000000 SECT6 notype () Внешний |? bar @@YAXXZ (void __cdecl bar (void))
dumpbin /SYMBOLS test1.obj
01D 00000000 SECT6 notype () Внешний |? baz @@YAXXZ (void __cdecl baz (void))
01E 00000000 SECT4 notype () Внешний |?? 0? $ Foo @ H @@ QAE @ H @ Z (public: __thiscall Foo:: Foo (int))
01F 00000000 SECT8 notype () Внешний |?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const)
В обоих *.obj файлах Foo присутствует.
Итак, мой вопрос в том, в чем может быть полезность явных деклараций создания экземпляров? Или, может быть, я что-то пропустил в своих тестах?
Я использую компилятор VS2013.
1 ответ
Вот хорошее объяснение, почему ATTEMP#2 не работает так, как я хочу: есть ли ошибка в шаблоне extern в Visual C++?
Короче говоря, когда вы определяете и внедряете шаблон в заголовочный файл, компилятор может встроить его. И тогда это делает явное определение экземпляра не работает по стандарту (14.7.2/10 "Явное создание").
Поэтому нам нужно заставить компилятор НЕ встроить шаблон. Например, реализуя его сразу после объявления.
template<class T>
class Foo {
...
T get() const;
};
template<class T>
T Foo<T>::get() const
{ ... }