You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
P1021R4の"Class Template Argument Deduction for alias templates"のセクションに、polymorphic_allocatorが導入される前にこの提案があったらpmr::vectorの宣言はこうなっていたのでは?的な事が書いてあって、そこで近い?使われ方をしています。
namespacepmr {
template<typename T>
using vector = std::vector<T, type_identity_t<pmr::polymorphic_allocator<T>>>; // See P0887R1 for type_identity_t
}
pmr::vector pv({1, 2, 3}, mem_res); // OK with this definition
Activity
onihusube commentedon Dec 17, 2020
P1021R4の"Class Template Argument Deduction for alias templates"のセクションに、
polymorphic_allocatorが導入される前にこの提案があったらpmr::vectorの宣言はこうなっていたのでは?的な事が書いてあって、そこで近い?使われ方をしています。テンプレートパラメータの推論対象外にする意図のようですが、よくわからない・・・
yumetodo commentedon Dec 17, 2020
https://stackoverflow.com/questions/31942048/when-to-use-the-identity-tmp-trick
にいろいろ書いてありますがうーん?
faithandbrave commentedon Dec 18, 2020
わからないのでstd-discussionに質問を投げてみました。
https://lists.isocpp.org/std-discussion/2020/12/0932.php
faithandbrave commentedon Dec 21, 2020
なるほど。
template <class T> void f(T a, T b);だとf(4.2, 1);でどちらの型に合わせればいいかわからなくて曖昧になるけど、template <class T> void f(T a, type_identity_t<T> b);だと第1引数の型に推論される、と。faithandbrave commentedon Dec 21, 2020
でも今回のケースは関係なさそうです。
faithandbrave commentedon Dec 21, 2020
polymorphic_allocatorの例は、mem_res引数から直接推論されてしまうとvector<T, memory_resource*>になってしまうけど、意図としてはmem_resを与えてもvector<T, polymorphic_allocator<T>>に推論されてほしいというもののようです。コンストラクタの第2引数になにが渡されようとも、クラステンプレートの第2テンプレートパラメータの型はすでに確定しているからコンストラクタ引数から推論はされず、
mem_resはpolymorphic_allocator<T>型の変換コンストラクタの引数として扱われる、と。規格のこの例は抽象的すぎて意味がわからないのと、とくに意味がないように思えるので、より具体的な例を紹介すればよさそうな気がします。
込み入った利用方法なので
pmr::vector以外のわかりやすい例を考えるのがつらいとこです。(だから規格はこんな例になってるのかもしれませんが)faithandbrave commentedon Dec 21, 2020
type_identity_tを通さない場合は、コンストラクタ引数{1, 2, 3}, mem_resを渡したいずれのコンストラクタ・推論ガイドの結果もmy_vector<T, std::pmr::polymorphic_allocator<T>>;型を導出しないので、曖昧になって推論失敗になるようです。onihusube commentedon Mar 27, 2021
std::type_identity_tを各種コンテナのアロケータパラメータに適用して、pmrコンテナの用法を改善する提案が出てました。ご参考に。onihusube commentedon Nov 11, 2021
ここでの
std::type_identity_tの使用は、エイリアステンプレートの推論補助を導出する際に推論できないコンテキストを意図的に作り出すことが目的で、それがあるときでも推論補助の導出が失敗しない・エイリアステンプレートの意味が変わるような推論補助とならない、ことを例示したいのだと思われます。規格書(提案文書)の最初に出てくる例、クラス
Cで推論補助を求めてみると、その有無で導出される推論補助が異なることがわかります。std::arrayのエイリアステンプレートとCTAD手順などの詳細は省略するので、この記事をご参照ください。
std::type_identity_tがある時今回は(1)の推論補助のみを対象に、推論補助の変換を行います。
まず(1)と(2)の右辺の対応から、テンプレートパラメータの対応を求めます。そこでは、(1)のテンプレートパラメータおよび右辺の型を引数とした関数テンプレート(
fとします)に対して、(2)の右辺の型を引数として与えた時の関数テンプレートのテンプレートパラメータ推論とほぼ同じことが行われます。この時、
fのテンプレートパラメータ推論によってT = V*の対応が求められます。一方、V*とstd::type_identity_t<U>からUを推論できない(推論できないコンテキスト)ので、Uに関する対応は得られません。次に、得たテンプレートパラメータの対応(
T = V*)とエイリアステンプレート(2)のテンプレートパラメータを推論補助(1)へフィードバックします。この推論補助は第一引数と第二引数が同じでなくても良く、それによって
CのT, Uも異なってしまい、(2)と意味が変わります。そこで、結果が一貫するように(正確には、推論できないコンテキストがあっため)追加の制約が必要になります。エイリアステンプレートの実引数を推定してエイリアステンプレートを書き戻す過程は省略し、これがエイリアステンプレート(2)に対して元の型の推論補助(1)を変換して得られる推論補助になります。
std::type_identity_tがない時まず(1)と(2)の右辺の対応から、テンプレートパラメータの対応を求めます。
ここでは、
T, Uともに推論可能なコンテキストなので、fのテンプレートパラメータT = U = V*と対応が求められます。次に、得たテンプレートパラメータの対応(
T = U = V*)とエイリアステンプレート(2)のテンプレートパラメータを推論補助(1)へフィードバックします。今回は推論できないコンテキストはなかったため追加の制約は不要で、これがエイリアステンプレート(2)に対して元の型の推論補助(1)を変換して得られる推論補助になります。
差
2つのケースを比べてみると
このように、
std::type_identity_tが推論補助右辺に現れていることによって(正確には、推論できないコンテキストが発生することで)、導出される推論補助は変化します。ただ、この場合は得られる結果は変化しないはずです。GCCのやること
GCCでは、推論補助を使用した時に推論に失敗してエラーとなると、エラーメッセージ中に使用した推論補助を出力してくれます。
std::type_identity_tがある場合 - [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッstd::type_identity_tがない場合 - [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッそこから拾って先ほどの場合にそれぞれどのような推論補助が使用されているかみてみると
となっており、手で求めたものと一致しており、やはり導出される推論補助が異なっていることが確認できます。
これらのことから、規格書およびP1814のサンプルコードで
std::type_identity_tが使用されているのは、推論補助導出過程で意図的に推論できないコンテキストを作り出すこと、それによって追加の制約が必要となること、その差で結果が変わらないこと、を例示しているのだと考察しました。