exponenta event banner

Правило AUTOSAR C++ 14 A14-8-2

Явные специализации шаблонов функций не должны использоваться

Описание

Определение правила

Явные специализации шаблонов функций не должны использоваться.

Объяснение

Явная специализация шаблонов функций может вызвать непредвиденные проблемы с разрешением перегрузки в C++. Разрешение перегрузки:

  • Сначала выполняется поиск нешаблонной простой старой функции, имеющей соответствующий список параметров.

  • Если такая функция недоступна, разрешение перегрузки выбирает ближайший шаблон совпадающей функции.

  • После выбора шаблона функции компилятор выполняет поиск подходящей специализации шаблона.

Специализация шаблона не изменяет порядок процесса разрешения перегрузки, что может привести к путанице и неожиданному поведению. Рассмотрим фрагмент кода:

//(a) base template
template<class T> void f( T ); 

//(b) specialization of (a)
template<> void f<>(int*);
//...

//(c) overloads (a)
template<class T> void f( T* );

//...
main(){
	int *p; 
	f( p );
}
Когда f() вызывается с помощью int* в main(), вы можете ожидать специализацию для int*, с пометкой (b), подлежит вызову. Компилятор разрешает вызов f() следующим образом:

  1. Компилятор выполняет поиск простой старой функции с типом ввода int*.

  2. Поскольку такой функции нет, компилятор выполняет поиск шаблона функции, имеющего ближайший совпадающий список параметров.

  3. Шаблон (c), который принимает общий указатель в качестве ввода, является ближайшим совпадением для f(int*).

  4. Шаблон (c) не является специализированным. Процесс разрешения перегрузки останавливается и вызывает шаблон в (c).

Несмотря на наличие специализированного шаблона для int* ввод типа определен в (b), перегрузка разрешается шаблону в (c), что может быть неожиданным.

При специализации шаблона перегруженной функции процесс разрешения перегрузки может стать более запутанным. Какой из перегруженных шаблонов становится специализированным, зависит от порядка объявления. Рассмотрим фрагмент кода:

//(a)
template <typename T> void F1(T t){}
//(b)
template <typename T> void F1(T* p){}
//(x): Specialization of template
template <> void F1<>(uint16_t* p){}
Вы не можете определить, специализируется ли (x) только на (a) или (b) из декларации. это зависит от порядка объявления. Например, в предыдущем случае (x) специализируется на (b). Но в этом случае (x) специализируется на (a):
//(a)
template <typename T> void F1(T t){}
//(x): Specialization of template
template <> void F1<>(uint16_t* p){}
//(b)
template <typename T> void F1(T* p){}

Чтобы избежать путаницы в коде и неожиданного поведения, избегайте специализированных шаблонов функций. Если необходимо специализировать шаблон функции, напишите один шаблон функции, который делегирует шаблону класса. Например, в этом коде шаблон функции f() делегаты в класс f_implementation.

template<class T> class f_implementation;

template<class T> void f( T t ) {
	FImpl<T>::f( t ); //Don't specialize function template
} 

template<class T> class f_implementation { 
	static void f( T t ); // Specializing class templates is permissible. 
}
Делегирование шаблону класса также обеспечивает частичную специализацию.

Внедрение Polyspace

При явной специализации шаблона функции Polyspace ® помечает шаблон функции.

Поиск неисправностей

Если вы ожидаете нарушения правила, но не видите его, обратитесь к разделу Нарушения стандартов кодирования не отображаются.

Примеры

развернуть все

В этом примере показано, как Polyspace помечает специализированные шаблоны функций.

#include <cstdint>
#include <memory>
#include <iostream>
//(a)
template <typename T> void F1(T t){
  std::cout << "(a)" << std::endl;
}
//(x) specializes (a)
template <> void F1<>(uint16_t* p){// Noncompliant
  std::cout << "(x)" << std::endl;
}
//(b) overloads (a)
template <typename T> void F1(T* p){// Compliant
  std::cout << "(b)" << std::endl; 
}
//(y) specializes (b)
template <> void F1<>(uint8_t* p){// Noncompliant
  std::cout << "(c)" << std::endl; 
}
//(d) plain old function overloads (a) and (b)
void F1(uint8_t* p){                    // Compliant
  std::cout << "(d)" << std::endl; 
}

int main(void)
{
	auto sp8 = std::make_unique<uint8_t>(3);
	auto sp16 = std::make_unique<uint16_t>(3);
	F1(sp8.get()); //calls (d), might expect (y) 
	F1(sp16.get()); //calls (b), might expect (x)
	return 0;
}

Когда функция F1() вызывается в основном, разрешение перегрузки определяет, какой экземпляр F1() вызывается.

  • Когда вызывается F1 () с указателями наuint8_tкомпилятор вызывает функцию plain-old-function (d), поскольку она имеет приоритет. Вы можете неправильно ожидать, что будет вызвана специализация (y).

  • Когда вызывается F1 () с указателями наuint16_tкомпилятор вызывает перегруженный шаблон (b), поскольку он является ближайшим соответствующим шаблоном. Вы можете неправильно ожидать, что будет вызвана специализация (x).

Специализация шаблонов функций может привести к путанице и неожиданным результатам. Polyspace помечает специализированные шаблоны функций (x) и (y).

Проверить информацию

Группа: Шаблоны
Категория: Обязательно, Автоматизировано
Представлен в R2020a