3

この質問に答えた後、私はこれを思いついた

シンプルな関数テンプレート (C++11) がありました。

template<class elem_t, class list_t>
bool in_list(const elem_t& elem, const list_t& list) {
   for (const auto& i : list) {
      if (elem == i) {
         return true;
      }
   }
   return false;
}

しかし、GCC はテンプレート パラメーターを std::initializer_list として推測することを好まないように見えるため、警告を発しました。だから、何も考えずに、私は専門化しました:

template<class elem_t>
bool in_list(const elem_t& elem, std::initializer_list<elem_t> list) {
   for (const auto& i : list) {
      if (elem == i) {
         return true;
      }
   }
   return false;
}

これはうまくいきました。もう警告はありません。しかし、改めて考えてみると、C++ は関数テンプレートの部分的なテンプレート特殊化をサポートしていないことを思い出しました。しかし、それはこれがそうであるように見えるものです。私の唯一の推測は、std::initializer_list がまだテンプレート パラメータに依存しているため、これが許可されているということです。したがって、本質的には別のテンプレートです。しかし、これが本来あるべき姿かどうかはわかりません (テンプレートが過負荷にならないという問題はありませんか?)。

これを受け入れるのは標準的な行動ですか?なぜ?

おまけの質問として、GCC がテンプレート パラメーターを std::initializer_list として推測するのを好まないのはなぜですか? コードをコピーして貼り付けて、パラメーターを std::initializer_list に置き換えるだけだと期待するのは、かなりばかげているようです。

警告メッセージ:

test.cpp: In function ‘int main()’:
test.cpp:33:43: warning: deducing ‘const list_t’ as ‘const std::initializer_list<int>’ [enabled by default]
test.cpp:6:6: warning:   in call to ‘bool in_list(const elem_t&, const list_t&) [with elem_t = int, list_t = std::initializer_list<int>]’ [enabled by default]
test.cpp:33:43: warning:   (you can disable this with -fno-deduce-init-list) [enabled by default]

から呼び出されたときin_list(3, {1, 2, 3, 4, 5});

編集: どうやらテンプレート パラメーターを initializer_list として推測することは、私のバージョンの GCC ( cite ) のワーキング ドラフトによる拡張です。新しい質問:これは最終的な c++11 標準の時点でまだ拡張機能ですか? もしそうなら、これは、標準に準拠したコードの 2 番目の関数を追加する必要があることを意味します。ご助力いただきありがとうございます!

EDIT2: GCC 4.7 ではコンパイラの方言フラグが削除されているように見えるため、問題は解決されたようですが、どのように解決されたかはわかりません。

4

2 に答える 2

4

他の回答のコメントで@Ben Voigtが言ったことを使用して、関連する標準的な引用をいくつか集めました。

§14.5.6.2

関数テンプレートは、他の関数テンプレートおよび通常の (非テンプレート) 関数でオーバーロードできます。通常の関数は、生成される可能性のある関数テンプレートの特殊化と同じ名前と型を持っていても、関数テンプレートとは関係ありません (つまり、特殊化とは見なされません) 。

したがって、2 つの関数テンプレートのオーバーロードが同じ関数を生成する可能性があるとしても、それは特殊化ではないため、関数テンプレートの特殊化を除外します。だから過積載です。

このような特殊化は別個の関数であり、1 つの定義規則 (3.2) に違反しません。

したがって、それらは別個の機能であり、それがエラーにならない理由です。

§14.5.6.2.1

関数テンプレートがオーバーロードされている場合、関数テンプレートの特殊化*の使用があいまいになる可能性があります。これは、テンプレートの引数推定 (14.8.2) によって関数テンプレートの特殊化が複数の関数テンプレート宣言に関連付けられる可能性があるためです。

これは、がin_list(a, b)両方の関数テンプレートに一致するように見えるということです。binitializer_list

(*ここでの「関数テンプレートの特殊化」とは、関数テンプレートを特殊化することを意味するのではなく、型でインスタンス化された関数テンプレートを意味することに注意してください。したがって、 withtemplate<typename T> f();f<int>()関数テンプレートの特殊化です。)

そこで、オーバーロードされた関数テンプレートの部分的な順序付けと呼ばれるものを使用して、これを解決します。

オーバーロードされた関数テンプレート宣言の部分的な順序付けは、次のコンテキストで使用され、関数テンプレートの特殊化が参照する関数テンプレートを選択します。

— 関数テンプレートの特殊化 (13.3.3) への呼び出しのオーバーロード解決中。

— 関数テンプレートの特殊化のアドレスが取得されたとき。

— 関数テンプレートの特殊化である配置演算子 delete が配置演算子 new と一致するように選択された場合 (3.7.4.2、5.3.4);

— フレンド関数宣言 (14.5.4)、明示的なインスタンス化 (14.7.2)、または明示的な特殊化 (14.7.3) が関数テンプレートの特殊化を参照する場合。

わかりましたので、それが部分的な順序付けです。これはそれが何をするかです:

部分的な順序付けでは、各テンプレートを順番に変換し (次の段落を参照)、関数の型を使用してテンプレートの引数推定を実行することにより、2 つの関数テンプレートのどちらが他方よりも特殊化されているかを選択します。推定プロセスは、テンプレートの 1 つが他のテンプレートよりも特化されているかどうかを判断します。その場合、より特殊化されたテンプレートが部分順序付けプロセスによって選択されます。

そして、どのテンプレートがより専門的であるかを判断するための長くて骨の折れるプロセスに入ります。それについては、必要に応じて読むことができますが、非常に複雑で、おそらくすべてを理解していない可能性があります (さらに、十分ではありません)。それについて書く時間です:))。

于 2012-04-29T18:05:30.127 に答える
3

それは部分的な仕様化ではありません。あなたがしていることは、関数のオーバーロードです。

于 2012-04-29T17:27:16.763 に答える