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

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

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

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
25
0
7 888
9

Ответы 9

Если специализированная функция шаблона также не указана в файле заголовка, другое приложение не будет знать о специализированной версии. Решением является добавление 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>() { ... }

Другие вопросы по теме