Есть ли ошибка в шаблоне extern в Visual C++?

Учитывая этот код:

//header.h
template <class T>
class Foo
{
public:
  Foo(T t) : t(t) {}
  T t;
};

//source1.cpp:
#include "header.h"
extern template class Foo<int>;
int main()
{
  Foo<int> f(42);
}

Насколько я понимаю, эта программа не должна ссылаться, так как не должно быть определения class Foo<int> в любом месте (extern template должен помешать этому). Однако с VC++ 11 (Visual Studio 2012) это компилируется и связывается. В GCC это не так:

source1.cpp:(.text+0x15): undefined reference to `Foo<int>::Foo(int)'

Однако, если я свяжусь с source2.cpp, он будет работать (как я и ожидал):

#include "header.h"
template class Foo<int>;

Согласно этому сообщению в блоге, внешний шаблон должен был поддерживаться начиная с VC10. http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx

Кстати, есть ли способ перечислить имена в объектном файле в Windows / Visual Studio? На Linux я бы сделал:

$ nm source1.o
U _ZN3FooIiEC1Ei      <- "U" means that this symbol is undefined.
0000000000000000 T main

1 ответ

Решение

C++11 14.7.2/10 "Явная реализация" говорит:

За исключением встроенных функций и специализаций шаблонов классов, явные объявления экземпляров подавляют неявное создание экземпляров сущности, к которой они относятся.

И конструктор в тебе шаблон класса Foo<T> встроенный VS2012 будет работать так, как вы ожидаете, если вы структурируете заголовок так:

//header.h
template <class T>
class Foo
{
public:
  Foo(T t);
  T t;
};

template <class T>
Foo<T>::Foo(T t) : t(t) 
{
}

так что конструктор не встроен.

Абзац из стандарта, который я цитировал выше, содержит следующее примечание:

[Примечание: намерение состоит в том, чтобы встроенная функция, являющаяся предметом явного объявления экземпляра, все еще неявно создавалась при использовании odr (3.2), так что тело можно рассматривать для встраивания, но чтобы не было внеплановой копии встроенной функции будет генерироваться в блоке перевода. - конец примечания]

Глядя на код сборки, созданный при вставке ctor, в объектный файл помещается внеплановая копия ctor (даже если ctor даже не вызывается, если вы компилируете пример с включенной оптимизацией), поэтому MSVC не похоже, не соответствует цели стандарта. Тем не менее, примечания не являются нормативными, поэтому я считаю, что поведение MSVC соответствует.


Что касается вашего побочного вопроса о сбросе символов из объектных файлов, созданных с помощью MSVC, вы можете использовать dumpbin полезность:

При компиляции примера с не встроенным конструктором:

dumpbin /symbols test.obj

...

008 00000000 UNDEF  notype ()    External     | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo<int>::Foo<int>(int))
             ^^^^^
...

Компилируем пример со встроенным ctor:

00A 00000000 SECT4  notype ()    External     | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo<int>::Foo<int>(int))
             ^^^^^
Другие вопросы по тегам