Почему положение явного экземпляра шаблона имеет значение

Скажем, я объявляю класс шаблона A в a.h

#include <iostream>

template<bool b>
class A { 
public:
  void print(std::ostream& out);
};

И определите метод печати в a.cpp (с явным указанием для true и false)

#include "a.h"

template<bool b>
void A<b>::print(std::ostream& out) {
  out << "A" << b;
}

template class A<true>;
template class A<false>;

Примером основной основной программы в main.cpp может быть

#include "a.h"

int main() {
  A<true> a;
  a.print(std::cout);
}

Небольшой проект выше компилируется просто отлично.

Вопрос: если я помещу явные экземпляры над определением метода printa.cpp), код больше не будет компилироваться с обычной ошибкой undefined reference to A<true>::print(...).

#include "a.h"

template class A<true>;
template class A<false>;

template<bool b>
void A<b>::print(std::ostream& out) {
  out << "A" << b;
}

Почему это так?

Изменить: Makefile для компиляции

main : main.o a.o
    g++ main.o a.o -o main

main.o : main.cpp
    g++ -c main.cpp

a.o : a.cpp 
    g++ -c a.cpp

person Dejan Jovanović    schedule 04.01.2013    source источник
comment
работает и на VS2012.   -  person billz    schedule 04.01.2013
comment
Я сделал, и я изменил порядок, как упомянул Деян   -  person billz    schedule 04.01.2013
comment
@jogojapan -- +1. Я удалил свой комментарий, в котором говорилось, что он компилируется с clang32 и gcc48. Он отлично компилировался с любым компилятором и в любом порядке, если содержимое трех файлов было вырезано и вставлено в один файл - как я изначально делал для своего теста. После того, как Деян выложил Makefile, я смог воспроизвести ошибку с clang и gcc любой версии.   -  person Leonid Volnitsky    schedule 04.01.2013
comment
@LeonidVolnitsky Хорошо, круто! Итак, я думаю, что только VS не реализует это правильно?   -  person jogojapan    schedule 04.01.2013


Ответы (2)


Я не думаю, что есть хорошее естественное объяснение тому, почему это так. Ясно, что компилятор может увидеть определение функции-члена, даже если оно предоставлено после явного создания экземпляра, поскольку оно находится в том же файле.

Однако для этого не требуются компиляторы; на самом деле это прямо запрещено Стандартом:

(§14.7.2/9) Явное определение создания экземпляра, которое называет специализацию шаблона класса, явно создает экземпляр специализации шаблона класса и является явным определением создания экземпляра только тех членов, которые были определены в момент создания экземпляра.

Я предполагаю, что причины этого включают следующее:

  • Может быть несколько отдельных явных специализаций для некоторых функций-членов позже в единице перевода; имеет смысл, в том числе и в интересах программиста, иметь явное правило о том, какие из них будут реализованы;

  • Когда шаблон создается неявно, учитываются только специализации, определенные до момента создания экземпляра; поэтому правило одинаково для неявных и явных экземпляров.

person jogojapan    schedule 04.01.2013

template class A<true>;
template class A<false>;

Та же причина, по которой обычно ожидается, что код шаблона определен в самом заголовке. Чтобы выполнить явное создание экземпляра, вы (компилятор) должны иметь возможность видеть полное определение класса шаблона, что невозможно из вашего main.cpp.

Однако a.cpp имеет доступ ко всем определениям класса (здесь метод печати), поэтому здесь работает явное создание экземпляров.

person Karthik T    schedule 04.01.2013
comment
Я не уверен, что понимаю ваш ответ. Меня смущает переупорядочение операторов в a.cpp. - person Dejan Jovanović; 04.01.2013
comment
@DejanJovanović: ах, мое недоразумение, тогда это похоже на компилятор, он работает на VS2010 - person Karthik T; 04.01.2013