Видимость типовой специализации функции С++

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

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

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

Ответ 1

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

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

extern template<> SomeFunc<int>();

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

Ответ 2

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

Я имею в виду где-то в файлеA.h

template<> SomeFunc<int>();

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

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

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

Ответ 9

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

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

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