Шаблон статической функции и 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):
Пространство имен без имени или пространство имен, объявленное прямо или косвенно в пространстве имен без имени, имеет внутреннюю связь.