43

C++11 標準のパラグラフ 14.8.2/8 は、置換の失敗が「ハード」コンパイル エラー (それによってコンパイルが失敗する) または「ソフト」エラーになる条件を指定しています。オーバーロード解決の候補のセットからコンパイラがテンプレートを破棄するようにします (コンパイルを失敗させず、よく知られている SFINAE イディオムを有効にします)。

置換の結果が無効な型または式になる場合、型推定は失敗します。無効な型または式は、置換された引数を使用して記述した場合に不適切な形式になるものです。[ 注: アクセス チェックは置換プロセスの一部として行われます。--end note ]関数の型とそのテンプレート パラメーターの型の直接のコンテキストで無効な型と式のみが、推論の失敗につながる可能性があります。[...]

「即時コンテキスト」という単語は、C++11 標準全体で 8 回しか現れず、そのたびに、次の (非規範的な) テキストのインスタンスが続く (またはその一部として現れる):

[注:置換された型と式の評価は、クラス テンプレートの特殊化および/または関数テンプレートの特殊化のインスタンス化、暗黙的に定義された関数の生成などの副作用をもたらす可能性があります。 context」であり、プログラムの形式が正しくない可能性があります。—終わりのメモ]

このメモは、 immediate contextが何を意味するかについて (あまり寛大ではない) ヒントを与えますが、少なくとも私にとっては、置換が「ハード」なコンパイル エラーを引き起こすかどうかを判断するのに十分ではありません。

質問:

関数型とそのテンプレート パラメーター型の "即時コンテキスト" で置換エラーが発生する場合と発生しない場合を理解するのに役立つ説明、決定手順、および/またはいくつかの具体的な例を提供していただけますか?

4

2 に答える 2

41

テンプレート引数置換の結果を決定するために必要なすべてのテンプレートと暗黙的に定義された関数を考慮し、置換が開始される前にそれらが最初に生成されると想像すると、その最初のステップで発生するエラーは直接のコンテキストにはありません。ハードエラーが発生します。

これらすべてのインスタンス化と暗黙的な定義 (削除された関数の定義を含む場合があります) がエラーなしで実行できる場合、置換中に (つまり、関数テンプレートのインスタンス化されたテンプレートと暗黙的に定義された関数を参照しているときに) 発生するさらなる「エラー」署名) はエラーではありませんが、推論の失敗につながります。

したがって、次のような関数テンプレートが与えられます。

template<typename T>
void
func(typename T::type* arg);

そして、他の関数で推論が失敗した場合に使用される「フォールバック」:

template<typename>
void
func(...);

そして、このようなクラス テンプレート:

template<typename T>
  struct A
  {
    typedef T* type;
  };

への呼び出しは、インスタンス化する必要がありますが存在するかどうかをチェックするためにfunc<A<int&>>(nullptr)代用A<int&>されます。への呼び出しの前に明示的なインスタンス化を配置することを想像すると、次のようになります。TT::typeA<int&>func<A<int&>(nullptr)

template class A<int&>;

型を作成しようとし、int&*参照へのポインターが許可されていないため、失敗します。のインスタンス化でハード エラーが発生するため、置換が成功したかどうかを確認する段階には至りませんA<int&>

の明示的な特殊化があるとしましょうA:

template<>
  struct A<char>
  {
  };

の呼び出しにfunc<A<char>>(nullptr)は のインスタンス化が必要A<char>なので、呼び出しの前にプログラムのどこかで明示的なインスタンス化を想像してみてください。

template class A<char>;

このインスタンス化は問題ありません。これによるエラーはありません。したがって、引数の置換に進みます。のインスタンス化はA<char>機能しましたが、A<char>::type存在しませんが、 の宣言でのみ参照されているため問題ありませんfunc。したがって、引数推定が失敗するだけで、...代わりにフォールバック関数が呼び出されます。

他の状況では、置換により、特別なメンバー関数が暗黙的に定義され、おそらく削除され、他のインスタンス化または暗黙的な定義がトリガーされる可能性があります。その「インスタンス化と暗黙の定義の生成」段階でエラーが発生した場合、それらはエラーですが、それが成功しても、置換中に関数テンプレートの署名の式が無効であることが判明した場合、たとえば、存在しないメンバーを使用したり、暗黙のうちに削除済みと定義された何か、それはエラーではなく、単なる推論の失敗です。

したがって、私が使用するメンタル モデルは、置換は最初に型とメンバーを生成するための「準備」ステップを実行する必要があるというものです。これによりハード エラーが発生する可能性がありますが、必要な生成がすべて完了すると、それ以上の無効な使用はエラーにはなりません。もちろん、これは問題を「即時コンテキストとはどういう意味ですか?」から移動させるだけです。「この置換をチェックする前に、どのタイプとメンバーを生成する必要がありますか?」だから、それはあなたを助けるかもしれないし、助けないかもしれません!

于 2013-03-07T01:04:06.283 に答える
7

即時コンテキストは、基本的にテンプレート宣言自体に表示されるものです。それ以外はすべてハード エラーです。ハードエラーの例:

#include <type_traits>

template<class T>
struct trait{ using type = typename T::type; };

template<class T, class U = typename trait<T>::type>
void f(int);
void f(...);

template<class T, class U = typename T::type>
void g(int);
void g(...);

template<class>
struct dependent_false : std::false_type{};

template<class T>
struct X{
    static_assert(dependent_false<T>(), "...");
    using type = void;
};

int main(){
    f<int>(0);
    g<X<int>>(0);
}

ライブバージョン。

于 2013-03-07T00:18:41.107 に答える