AUTOSAR C++14 Rule 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* type input задан в (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компилятор вызывает обычную старую функцию (d), поскольку она имеет приоритет. Вы можете неправильно ожидать вызова специализации (y).

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

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

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

Группа: Шаблоны
Категория: Необходимый, Автоматизированный
Введенный в R2020a