Шаблон статической функции и 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++ Checker жалуется:

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

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

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

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

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

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 в качестве лучшего соответствия. NB. Явная специализация не принимала участия ни в одном из вышеуказанных этапов.

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

Относительно цитируемого абзаца:

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

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

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

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

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

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