Следующий шаблон функции со специализациями должен использоваться в том же .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 жалуется:
Я попытался выяснить, что не так, и нашел подсказку в Стандартная цитата C ++:
Для вызова функции, который зависит от параметра шаблона, если
имя функции является безусловным идентификатором, но не идентификатором шаблона,
Функции-кандидаты находятся с использованием обычных правил поиска (3.4.1,
3.4.2) за исключением того, что:
- Только для части поиска с использованием поиска без определения имени (3.4.1)
объявления функций с внешняя связь из шаблона
Определение контекста найдено.
Означает ли это, что компиляторы отбрасывают static
спецификация и нет никакого способа на самом деле сделать шаблоны статических функций в C ++ 03?
Явные специализации позволяют изменять определение функции (или класса) на основе аргументов шаблона, с которыми специализирован шаблон. Они не являются «новыми декларациями».
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, лучший вариант — следовать рекомендациям металла комментарий к вашему вопросу. Поместите шаблон в безымянное пространство имен.
В C ++ ’03 имена будут не вызываться извне модуля перевода, а для C ++ ’11 и далее они неявно имеют внутреннюю связь (3.5 / 4):
Неназванное пространство имен или пространство имен, объявленное прямо или косвенно в безымянном пространстве имен, имеет
внутренняя связь.