Шаблонный оператор‹‹ явное создание экземпляра и заголовок

Как правило, для моих шаблонных классов я включаю объявления в файл .hpp, а код реализации шаблона — в файл .t.hpp. Я явно создаю экземпляр класса в файле .cpp:

template class MyClass< AnotherClass >;

чей объектный код помещается в библиотеку.

Проблема в том, что если я попытаюсь напечатать объект с operator<<, который объявлен в файле .hpp и определен в файле .t.hpp как:

template<class T>
std::ostream& operator<<( std::ostream& os, const MyClass<T>& c)
{
    os  << "Hello, I am being output.";
    return os;
}

Я получаю сообщение об ошибке компоновщика о том, что правильный символ не определен.

Я понимаю, что это связано с тем, что эта шаблонная функция не создается явно, когда класс. Есть ли способ обойти это, кроме как включать файл .t.hpp каждый раз, когда я хочу использовать operator<< в классе, или перемещать шаблонный код функции в файл .hpp? Могу ли я явно создать экземпляр кода функции?


person Seth Johnson    schedule 18.06.2009    source источник


Ответы (2)


Вы можете явно создавать экземпляры шаблонов функций

template std::ostream& operator<<(std::ostream&, const MyClass<int>&);

Чтобы создать экземпляр специализации с помощью T = int. Скобки аргументов шаблона можно опустить, если все аргументы шаблона могут быть выведены (как в этом случае, из типа MyClass<int>). Если это невозможно, например, потому что параметры шаблона не встречаются в типе параметра функции, вы должны явно указать это

template<typename T> void f() { }
template void f<int>();
person Johannes Schaub - litb    schedule 18.06.2009
comment
+1. Я только что добавил невыведенный случай и вижу, что вы уже обновили его, чтобы включить его... Грр! :) - person j_random_hacker; 18.06.2009
comment
Спасибо! Это как раз то, что мне было нужно. Я не знал, что шаблонные функции могут быть созданы явно. - person Seth Johnson; 19.06.2009

См. решение litb.

В вашем конкретном случае компилятор может вывести все аргументы шаблона из типов параметров и возвращаемых значений, но если это невозможно, вы можете указать их явно:

template std::ostream& operator<< <T>(std::ostream&, const MyClass<T>&);

Это по-прежнему разрешено, если можно вывести аргументы шаблона.

Могу я спросить: есть ли причина, по которой вы предпочитаете явно создавать экземпляры всего? Вы делаете довольно много работы для себя. Вы, безусловно, сэкономите время компиляции, избегая включения исходного кода шаблона повсюду, но стоит ли это времени разработчика?

person j_random_hacker    schedule 18.06.2009
comment
У меня точно такой же вопрос. ИМХО в большинстве случаев время разработчика › время выполнения › время компиляции. - person Shing Yip; 18.06.2009
comment
Это для научного вычислительного кода. Большая часть моего кода основана на размерности: т. е. параметр шаблона представляет собой беззнаковое целое число, которое может быть 1, 2 или 3 в зависимости от того, выполняю ли я одномерную задачу или что-то еще. Время выполнения имеет первостепенное значение, и немного дополнительного времени разработчика (поскольку мне практически никогда не придется изменять параметр шаблона или добавлять дополнительные классы) стоит дополнительного времени компиляции: я архивирую окончательный шаблонный код в статическую библиотеку, которая связан с различными внешними интерфейсами, модульными тестами и т. д. - person Seth Johnson; 19.06.2009
comment
@Seth: Время выполнения для явно созданного кода никогда не будет лучше, чем для неявно созданного кода, а часто и хуже, потому что компилятор не может выполнить встраивание - для этого ему нужно увидеть исходный код шаблона. Это может иметь большое значение в тугих петлях. (Но вы все равно должны использовать существующую библиотеку, настроенную для процессора, для числовых примитивов, таких как умножение матриц, поскольку они обычно намного быстрее.) - person j_random_hacker; 19.06.2009
comment
@j_random_hacker: я не хотел предполагать, что явное и неявное создание экземпляров изменит время выполнения (для кода, который не указан как встроенный). Я обязательно встраиваю все, что вызывается часто, особенно в узких внутренних циклах; только самые внешние объекты создаются явно. И я использую настроенные библиотеки других людей, когда могу. - person Seth Johnson; 24.06.2009