メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

MISRA C++:2023 Rule 17.8.1

Function templates shall not be explicitly specialized

R2024b 以降

説明

ルール定義

Function templates shall not be explicitly specialized. 1

根拠

関数テンプレートの明示的特殊化は、C++ でのオーバーロード解決に伴う予期せぬ問題を引き起こす可能性があります。オーバーロード解決:

  • まず、一致するパラメーター リストをもつ非テンプレートの plain old 関数を探します。

  • このような関数が見つからない場合は、オーバーロード解決が最も近い関数テンプレートを選択します。

  • 関数テンプレートを選択したら、コンパイラは選択されたテンプレートの適切な特殊化を検索します。

テンプレートの特殊化では、オーバーロード解決プロセスの順序が変更されないため、予期せず、理解できない動作が生じる可能性があります。次のコード スニペットを考えてみましょう。

//(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()main()int* を使用して呼び出される場合は、(b) でマークされている int* の特殊化が呼び出されると想定します。コンパイラは、次のように、f() の呼び出しを解決します。

  1. コンパイラは、入力型が int* の plain old 関数を検索します。

  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 から報告されない場合は、コーディング規約違反が想定どおりに表示されない理由の診断を参照してください。

すべて展開する

この例は、Polyspace が特殊化された関数テンプレートにどのようにフラグを設定するかを示します。main() で関数 F1() が呼び出されると、オーバーロード解決が F1() のどのインスタンスが呼び出されるかを判断します。

  • F1() が uint8_t へのポインターを使用して呼び出されると、コンパイラは優先順位の高い plain old 関数 (d) を呼び出します。特殊化 (y) が呼び出されると想定していたかもしれません。

  • F1() が uint16_t へのポインターを使用して呼び出されると、コンパイラは最も近いテンプレートであるオーバーロードされたテンプレート (b) を呼び出します。特殊化 (x) が呼び出されると想定していたかもしれません。

関数テンプレートの特殊化では、分かりづらく、予期しない結果が生じる可能性があります。Polyspace は、特殊化されたテンプレートの (x) と (y) にフラグを設定します。

#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;
}

チェック情報

グループ: Templates
カテゴリ: Required

バージョン履歴

R2024b で導入

すべて展開する


1 All MISRA coding rules and directives are © Copyright The MISRA Consortium Limited 2021.

The MISRA coding standards referenced in the Polyspace Bug Finder™ documentation are from the following MISRA standards:

  • MISRA C:2004

  • MISRA C:2012

  • MISRA C:2023

  • MISRA C++:2008

  • MISRA C++:2023

MISRA and MISRA C are registered trademarks of The MISRA Consortium Limited 2021.