Видимость шаблонной специализации функции C ++ - PullRequest
26 голосов
/ 12 сентября 2008

Предположим, у меня есть fileA.h, который объявляет класс classA с функцией шаблона SomeFunc<T>(). Эта функция реализована непосредственно в заголовочном файле (как обычно для шаблонных функций). Теперь я добавляю специализированную реализацию SomeFunc() (как для SomeFunc<int>()) в fileA.C (т.е. не в заголовочном файле).

Если я сейчас позвоню SomeFunc<int>() из какого-то другого кода (возможно, также из другой библиотеки), будет ли он называть универсальную версию или специализацию?

У меня есть эта проблема прямо сейчас, когда класс и функция живут в библиотеке, которая используется двумя приложениями. И одно приложение правильно использует специализацию, в то время как другое приложение использует общую форму (что впоследствии вызывает проблемы во время выполнения). Почему разница? Может ли это быть связано с настройками компоновщика и т. Д.? Это в Linux с g ++ 4.1.2.

Ответы [ 9 ]

22 голосов
/ 12 сентября 2008

Это ошибка иметь специализацию для шаблона, который не виден в точке вызова. К сожалению, компиляторы не обязаны диагностировать эту ошибку, а затем могут делать то, что им нравится, с вашим кодом (в стандарте он «плохо сформирован, диагностика не требуется»).

Технически, вам нужно определить специализацию в заголовочном файле, но почти каждый компилятор будет обрабатывать это так, как вы могли ожидать: это исправлено в C ++ 11 с новым средством "extern template":

extern template<> SomeFunc<int>();

Это явно заявляет, что конкретная специализация определена в другом месте. Многие компиляторы уже поддерживают это, некоторые с, а некоторые без extern.

9 голосов
/ 12 сентября 2008

Вы добавили прототип с параметрами в заголовочный файл?

Я имею в виду, есть ли где-нибудь в файле A.h

template<> SomeFunc<int>();

Если нет, то, вероятно, причина.

3 голосов
/ 24 апреля 2009

У меня была такая же проблема с 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";
    }

2 голосов
/ 12 сентября 2008

Согласно спецификациям, шаблон вашей специализированной функции никогда не должен вызываться за пределами fileA.C, если только вы не export определение шаблона, которое в настоящее время не поддерживается ни одним компилятором (кроме Comeau) (или не запланировано на обозримое будущее).

С другой стороны, после создания экземпляра шаблона функции для компилятора становится видимой функция, которая больше не является шаблоном. GCC может повторно использовать это определение в разных блоках компилятора, поскольку стандарт гласит, что каждый шаблон должен создаваться только один раз для данного набора аргументов типа [temp.spec]. Тем не менее, поскольку шаблон не экспортируется, это должно быть ограничено модулем компиляции.

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

1 голос
/ 11 сентября 2013

Как говорит Энтони Уильямс, конструкция 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>() { ... }
0 голосов
/ 12 сентября 2008

@ [Антони-Вильямса]

Вы уверены, что не путаете extern объявления шаблонов с extern template экземплярами? Из того, что я вижу, extern template может только использоваться для явной реализации, а не для специализации (что подразумевает неявную реализацию) [temp.expl.spec] не упоминает ключевое слово extern:

явной специализации
template <> декларация

0 голосов
/ 12 сентября 2008

В Microsoft C ++ я провел эксперимент со встроенными функциями. Я хотел знать, что произойдет, если я определю несовместимые версии функции в разных источниках. Я получил разные результаты в зависимости от того, использовал ли я сборку Debug или сборку Release. В Debug компилятор отказывается что-либо встроить, и компоновщик связывает одну и ту же версию функции независимо от того, что находится в области действия в источнике. В Release, компилятор указывал, какая версия была определена в то время, и у вас были разные версии функции.

Ни в одном из случаев не было никаких предупреждений. Я как бы подозревал это, поэтому я и провел эксперимент.

Я предполагаю, что функции шаблона будут вести себя так же, как и другие компиляторы.

0 голосов
/ 12 сентября 2008

Брэндон: это то, что я подумал - никогда нельзя вызывать специализированную функцию. Что верно для второго приложения, которое я упомянул. Однако первое приложение явно вызывает специализированную форму, даже если специализация не объявлена ​​в заголовочном файле!

Здесь я в основном стремлюсь к просветлению :-), потому что первое приложение - это юнит-тест, и, к сожалению, есть ошибка, которая не появляется в тесте, но в реальном приложении ...

(PS: я исправил эту конкретную ошибку, действительно, объявив специализацию в заголовке; но какие другие подобные ошибки все еще могут быть скрыты?)

0 голосов
/ 12 сентября 2008

Если функция специализированного шаблона также не указана в заголовочном файле, другое приложение не будет знать о специализированной версии. Решением является добавление SomeFunc<int>() к заголовку.

...