C++ 14では、テンプレート化された変数を作成できます。通常の例は、さまざまなタイプの数学定数πの値を取得するために読み取ることができる変数「pi」です(int
の場合は3、float
で可能な限り最も近い値など)。
それに加えて、テンプレート化された構造体またはクラス内で変数をラップするだけでこの機能を使用できますが、これを型変換とどのように組み合わせますか?オーバーラップが見られます。
Piの例以外に、非const変数でどのように機能しますか?そのような機能を最大限に活用する方法とその目的を理解するための使用例はありますか?
Piの例以外に、非const変数でどのように機能しますか?
現在、タイプごとに変数を個別にインスタンス化するようです。つまり、_n<int>
_に10を割り当てることができますが、これはテンプレート定義とは異なります。
_template<typename T>
T n = T(5);
int main()
{
n<int> = 10;
std::cout << n<int> << " "; // 10
std::cout << n<double> << " "; // 5
}
_
宣言がconst
の場合、読み取り専用です。すべてのconstexpr
宣言のようにconstexpr
である場合、constexpr
(ressions)以外ではあまり使用しません。
それに加えて、テンプレート化された構造体またはクラス内に変数をラップするだけでこの機能を使用できますが、これを型変換とどのように組み合わせますか?
それは、単純な提案であることを意図しています。型変換にどのように影響するか、重要な方法で確認できません。既に述べたように、変数の型はテンプレートをインスタンス化した型です。つまり、decltype(n<int>)
はintです。 decltype((double)n<int>)
はdoubleなどです。
そのような機能を最大限に活用する方法とその目的を理解するための使用例はありますか?
N3651 は簡潔な根拠を提供します。
残念ながら、既存のC++ルールでは、テンプレート宣言で変数を宣言することはできません。この問題に対する既知の回避策があります。
•クラステンプレートのconstexpr静的データメンバーを使用する
•目的の値を返すconstexpr関数テンプレートを使用します
これらの回避策は何十年も知られており、十分に文書化されています。 std :: numeric_limitsなどの標準クラスは、典型的な例です。これらの回避策は完全ではありませんが、C++ 03時代には単純で組み込み型の定数のみが制限なく直接かつ効率的なコンパイル時間のサポートを享受していたため、それらの欠点はある程度許容されました。これらはすべて、C++ 11のconstexpr変数の採用によって変化し、ユーザー定義型の定数に対する直接かつ効率的なサポートが拡張されました。現在、プログラマーは(クラス型の)定数をプログラムでますます明らかにしています。そのため、回避策に関連する混乱とフラストレーションを増やします。
...
「静的データメンバー」の主な問題は次のとおりです。
•「重複」宣言が必要です。定数がODRで使用される場合に、「実際の」定義を提供するために、クラステンプレート内に一度、クラステンプレート外に一度。
•プログラマは、同じ宣言を2回提供する必要性に困惑し、混乱しています。対照的に、「通常の」定数宣言は重複した宣言を必要としません。
...
このカテゴリのよく知られた例は、おそらくnumeric_limitsの静的メンバー関数、または
boost::constants::pi<T>()
などの関数です。constexpr関数テンプレートは、静的データメンバーが持つ「重複宣言」の問題を抱えていません。さらに、それらは機能的な抽象化を提供します。ただし、定義サイトで、定数をどのように配信するかを、const参照または単純な非参照型のいずれかで事前に選択することをプログラマに強制します。 const参照によって配信される場合、定数は静的ストレージに体系的に割り当てられる必要があります。非参照型の場合、定数をコピーする必要があります。コピーは組み込み型の問題ではありませんが、小さな組み込み型(マトリックス、整数、またはビッグフロートなど)の単なるラッパーではない値セマンティクスを持つユーザー定義型のショートップです。通常の "const(expr)変数はこの問題の影響を受けません。単純な定義が提供され、定数をストレージに実際にレイアウトする必要があるかどうかの決定は、定義ではなく、使用法のみに依存します。
テンプレート化された構造体またはクラス内に変数をラップするだけで、この機能を使用できます
はい、しかしそれは無意味な統語論的な塩でしょう。血圧が低い。
pi<double>
は、pi<double>::value
。短いとポイント。私の本では、この構文を許可し奨励するのに十分な理由です。
C++ 14の変数テンプレートのもう1つの実用的な例は、std::accumulate
に何かを渡すための関数が必要な場合です。
template<typename T>
T const & (*maxer) (T const &, T const &) = std::max<T>;
std::accumulate(some.begin(), some.end(), initial, maxer<float>);
std::max<T>
の使用は、正確な署名を推測できないため不十分であることに注意してください。この特定の例では、代わりにmax_element
を使用できますが、ポイントは、この動作を共有する関数のクラス全体があることです。
これらの線に沿って何かが可能かどうか疑問に思います:(テンプレートラムダの可用性を想定)
_void some_func() {
template<typename T>
std::map<int, T> storage;
auto store = []<typename T>(int key, const T& value) { storage<T>[key] = value; };
store(0, 2);
store(1, "Hello"s);
store(2, 0.7);
// All three values are stored in a different map, according to their type.
}
_
さて、これは便利ですか?
より簡単な使用法として、_pi<T>
_の初期化では、均一な初期化ではなく、明示的な変換(単項コンストラクターの明示的な呼び出し)が使用されることに注意してください。つまり、タイプradians
にコンストラクタradians(double)
を指定すると、_pi<radians>
_を記述できます。
これを使用して、次のようなコンパイル時コードを作成できます。
#include <iostream>
template <int N> const int ctSquare = N*N;
int main() {
std::cout << ctSquare<7> << std::endl;
}
これは同等のものよりも大幅に改善されています
#include <iostream>
template <int N> struct ctSquare {
static const int value = N*N;
};
int main() {
std::cout << ctSquare<7>::value << std::endl;
}
人々が変数テンプレートが導入される前にテンプレートのメタプログラミングを実行するために書いていた。型以外の値については、C++ 11でconstexpr
を使用してこれを行うことができたため、テンプレート変数には、変数テンプレートの型に基づいた計算ができるという利点しかありません。
TL; DR:以前はできなかったことはできませんが、テンプレートのメタプログラミングをPITAより少なくします。