Шаблон статической функции и MISRA C++

Следующий шаблон функции со специализациями должен использоваться только в одном файле .cpp, поэтому я хотел бы сделать его static. Следующий код компилируется (без предупреждений) с использованием как MS Visual C++ 2008, так и GCC 4.8.1 и работает так, как предполагалось. (Добавление static в начало строк 5 и 11 приведет к тому, что GCC выдаст ошибку, но не MSVC.)

 1  template <class T>
 2  static bool foo(const T param);
 3
 4  template <>
 5  bool foo<int>(const int param)
 6  {
 7      return doSomethingWithInt(param);
 8  }
 9
10  template <>
11  bool foo<bool>(const bool param)
12  {
13      return doSomethingWithBool(param);
14  }

Однако средство проверки MISRA C++ жалуется:

  • (MISRA2008.3-3-2) Применение статического ключевого слова к объявлению 'foo' (1)
  • (MISRA2008.3-3-2) Применение статического ключевого слова к объявлению «foo» (5)
  • (MISRA2008.2-10-5-b) Идентификатор 'foo' используется повторно (5)
  • (MISRA2008.3-3-2) Применение статического ключевого слова к объявлению 'foo' (11)
  • (MISRA2008.2-10-5-b) Идентификатор 'foo' используется повторно (11)

Я попытался выяснить, что не так, и нашел подсказку в стандартной цитате C++:

Для вызова функции, который зависит от параметра шаблона, если имя функции является неполным идентификатором, но не идентификатором шаблона, функции-кандидаты находятся с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением того, что:

  • Для части поиска, использующей поиск по неполному имени (3.4.1), обнаруживаются только объявления функций с внешней связью из контекста определения шаблона.

Означает ли это, что компиляторы отбрасывают спецификацию static и нет возможности создавать шаблоны статических функций в C++03?


person Melebius    schedule 11.09.2014    source источник
comment
Вы можете поместить его в безымянное пространство имен: namespace { template<class T> void Foo() { ... } }. Не уверен, что средство проверки MISRA достаточно умно, чтобы знать, что оно сделает то же самое.   -  person metal    schedule 11.09.2014
comment
В этом случае на самом деле нелогично определять явные специализации шаблона. Синтаксис перегрузки обычных функций должен быть более понятным, и добавление static будет работать.   -  person jxh    schedule 12.09.2014
comment
@jxh Ну, это должно работать с приведенным выше примером, но моя реальная проблема немного сложнее. Я вызываю свой шаблон статической функции из другого шаблона функции, который не является специализированным, и в вызове используется явный параметр шаблона, поскольку параметр, различающий тип, отсутствует.   -  person Melebius    schedule 15.09.2014
comment
Какой MISRA checker вы используете?   -  person Andrew    schedule 06.11.2014
comment
@Эндрю Парасофт С++ тест 9.2.   -  person Melebius    schedule 06.11.2014
comment
Отвечает ли это на ваш вопрос? Полное руководство и список книг по C++   -  person pforpraphul    schedule 04.05.2020
comment
@pforpraphul Нет, это довольно конкретный вопрос. Как ваше предложение распространяется на правила MISRA?   -  person Melebius    schedule 04.05.2020


Ответы (1)


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

GCC правильно предупреждает об использовании static в явных специализациях 7.1.1/1:

Спецификатор класса хранения не должен указываться в директиве явной специализации (14.7.3) или явной инстанциации (14.7.2).

Таким образом, кажется, что совет от вашего средства проверки MISRA применить «статический» неверен для 5 и 11, и я также усомнился бы в том, что foo каким-то образом используется повторно. Есть только одна сущность foo, которая имеет разные определения.

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

Рассмотрим следующее:

template <typename T>
void f (T);              // #1

template <>
void f<int*> (int*);     // #2

template <typename T>
void f (T*);             // #3

void b() {
  int * i;
  f(i);                  // Calls #3
}

Поиск f находит два шаблона: №1 — f(T) и №3 — f(T*). T выводится как int* для №1 и int для №3 (14.8.2). Разрешение перегрузки происходит со специализациями №1 -> f(int*) и №3 -> f(int*). Ни одно из них не является лучшим совпадением, поэтому имеет место частичный порядок (14.5.6.2). Результатом частичного упорядочения является то, что № 3 более специализирован, чем № 1. Поэтому компилятор выбирает #3 как наилучшее совпадение. Примечание. Явная специализация не участвовала ни в одном из вышеперечисленных шагов.

Если бы не номер 3. Тогда номер 1 был бы выбран как наиболее подходящий по разрешению перегрузки. Затем компилятор просматривает список специализаций. Выведенный список аргументов int* соответствует списку аргументов, используемому в явной спецификации, поэтому вызывается определение №2.

По поводу процитированного абзаца:

  • Для части поиска, использующей поиск по неполному имени (3.4.1), обнаруживаются только объявления функций с внешней связью из контекста определения шаблона.

Это ограничение возникло еще тогда, когда можно было экспортировать шаблоны (C++ '03 14/6). Идея заключалась в том, чтобы позволить определять шаблоны за пределами текущей единицы перевода. Это ограничение поиска помогло бы гарантировать, что изменение неэкспортируемого шаблона для экспорта не приведет к другой смысловой программе.

Что касается вашего вопроса о том, что это означает для шаблонов статических функций и C++ '03, то реальность такова, что только один поставщик компилятора, которого я знаю, когда-либо полностью реализовывал экспортированные шаблоны. Есть хороший шанс, что большинство производителей компиляторов в любом случае уже давно следовали совету C++ 11. С точки зрения соответствия требованиям MISRA, лучше всего следовать советам в комментарии металла к вашему вопросу. Поместите шаблон в безымянное пространство имен.

В C++ '03 имена нельзя будет вызывать извне единицы перевода, а для C++ '11 и далее они имеют неявную внутреннюю связь (3.5/4):

Безымянное пространство имен или пространство имен, объявленное прямо или косвенно в безымянном пространстве имен, имеет внутреннюю связь.

person Richard Corden    schedule 27.07.2015