Предположим, у меня есть fileA.h
, который объявляет класс classA
с функцией шаблона SomeFunc<T>()
. Эта функция реализована непосредственно в файле заголовка (как обычно для функций шаблона). Теперь я добавляю специализированную реализацию SomeFunc()
(например, SomeFunc<int>()
) в fileA.C
(т.е. не в файл заголовка).
Если я сейчас вызову SomeFunc<int>()
из другого кода (возможно, также из другой библиотеки), вызовет ли он общую версию или специализацию?
У меня есть эта проблема прямо сейчас, когда класс и функция находятся в библиотеке, которая используется двумя приложениями. И одно приложение правильно использует специализацию, в то время как другое приложение использует универсальную форму (что впоследствии вызывает проблемы во время выполнения). В чем разница? Может ли это быть связано с параметрами компоновщика и т. д.? Это в Linux с g ++ 4.1.2.
Если специализированная функция шаблона также не указана в файле заголовка, другое приложение не будет знать о специализированной версии. Решением является добавление SomeFunc<int>()
в заголовок.
Вы добавили прототип с параметрами в свой заголовочный файл?
Я имею в виду, есть ли где-нибудь в файле A.h
template<> SomeFunc<int>();
Если нет, то, вероятно, причина.
Брэндон: я так и думал - специализированную функцию вызывать не следует. Что верно для второго упомянутого приложения. Однако первое приложение явно вызывает специализированную форму, даже если специализация не объявлена в файле заголовка!
Я в основном ищу просветления здесь :-), потому что первое приложение - это модульный тест, и, к сожалению, есть ошибка, которая появляется не в тесте, а в реальном приложении ...
(PS: я исправил эту конкретную ошибку, действительно, объявив специализацию в заголовке; но какие другие похожие ошибки могут быть скрыты?)
Согласно спецификациям, ваш специализированный шаблон функции никогда не должен вызываться за пределами fileA.C
, если вы не export
определение шаблона, которое в настоящее время не поддерживает ни один компилятор (кроме Comeau) (или не планируется в обозримом будущем).
С другой стороны, как только шаблон функции создается, компилятору становится видна функция, которая больше не является шаблоном. GCC может повторно использовать это определение в разных модулях компилятора, поскольку в стандарте указано, что каждый шаблон должен быть создан только один раз для заданного набора аргументов типа [temp.spec]. Тем не менее, поскольку шаблон не экспортируется, это должно быть ограничено единицей компиляции.
Я считаю, что GCC может выявить здесь ошибку при совместном использовании списка созданных шаблонов между единицами компиляции. Обычно это разумная оптимизация, но она должна учитывать специализацию функций, что, по-видимому, не делает правильно.
В Microsoft C++ я провел эксперимент со встроенными функциями. Я хотел знать, что произойдет, если я определю несовместимые версии функции в разных источниках. Я получил разные результаты в зависимости от того, использовал ли я сборку отладки или сборку выпуска. В Debug компилятор отказывается вставлять что-либо, а компоновщик связывал одну и ту же версию функции независимо от того, что было в области видимости в источнике. В Release компилятор встроил ту версию, которая была определена в то время, и вы получили разные версии функции.
Ни в том, ни в другом случае предупреждений не было. Я как бы подозревал это, поэтому и провел эксперимент.
Я предполагаю, что функции шаблона будут вести себя так же, как и другие компиляторы.
Это ошибка, чтобы иметь специализацию для шаблона, которая не видна в точке вызова. К сожалению, компиляторы не обязаны диагностировать эту ошибку, а затем могут делать с вашим кодом все, что им заблагорассудится (в стандартном выражении это «плохо сформировано, диагностика не требуется»).
Технически вам нужно определить специализацию в файле заголовка, но почти каждый компилятор будет обрабатывать это, как и следовало ожидать: это исправлено в C++ 11 с помощью новой возможности "extern template":
extern template<> SomeFunc<int>();
Это явно заявляет, что конкретная специализация определена в другом месте. Многие компиляторы уже поддерживают это, некоторые с extern
, а некоторые без него.
@ [Энтони-Уильямс],
Вы уверены, что не путаете объявления шаблона extern
с экземплярами extern template
? Из того, что я вижу, extern template
может использовать Только для явного создания экземпляра, а не для специализации (что подразумевает неявное создание экземпляра). [temp.expl.spec] не упоминает ключевое слово extern
:
explicit-specialization:
template
< > declaration
У меня была такая же проблема с gcc4, вот как я ее решил. Это было более простое решение, чем то, во что я мог поверить в предыдущих комментариях. Идеи предыдущих постов были правильными, но их синтаксис мне не подошел.
----------header-----------------
template < class A >
void foobar(A& object)
{
std::cout << object;
}
template <>
void foobar(int);
---------source------------------
#include "header.hpp"
template <>
void foobar(int x)
{
std::cout << "an int";
}
Как говорит Энтони Уильямс, конструкция extern template
- правильный способ сделать это, но, поскольку его пример кода является неполным и имеет несколько синтаксических ошибок, вот полное решение.
fileA.h:
namespace myNamespace {
class classA {
public:
template <class T> void SomeFunc() { ... }
};
// The following line declares the specialization SomeFunc<int>().
template <> void classA::SomeFunc<int>();
// The following line externalizes the instantiation of the previously
// declared specialization SomeFunc<int>(). If the preceding line is omitted,
// the following line PREVENTS the specialization of SomeFunc<int>();
// SomeFunc<int>() will not be usable unless it is manually instantiated
// separately). When the preceding line is included, all the compilers I
// tested this on, including gcc, behave exactly the same (throwing a link
// error if the specialization of SomeFunc<int>() is not instantiated
// separately), regardless of whether or not the following line is included;
// however, my understanding is that nothing in the standard requires that
// behavior if the following line is NOT included.
extern template void classA::SomeFunc<int>();
}
fileA.C:
#include "fileA.h"
template <> void myNamespace::classA::SomeFunc<int>() { ... }