ユニバーサルオートデザイナー

C ++ 11の出現により、auto型の変数を宣言することが可能になり、初期化される値の型に基づいて、コンパイラ自体が変数の実際の型を決定しました。 これは、不明であるか、あまり重要ではない、または単純にするために、型が複雑すぎる変数を初期化する場合に便利です。

例:

auto f = [](){}; //   auto r = foo(10); //   foo for (auto i = 0; i < 10; i++){} 

...など つまり、等式の左側には自動型autoがあり、右側には明確に定義された型の値があります。 今、私たちが反対を持っていると想像してください:

 int a = auto(10); 

左側には明確に記述されたタイプがあり、右側には未知のものがあります。 もちろん、この例では、単にaの値を10に設定できる場合、ユニバーサルコンストラクターを呼び出すことは意味がありません。

 int a = 10; 

または極端な場合、コンストラクターを呼び出します。

 int a(10); 

そして、それが関数の引数であれば、例えば:

 str::map<char, int> myMap; myMap.insert(pair<char, int>('a', 10)); 

マップテンプレートクラスのinsertメソッドは、明確に定義された型を想定していますが、呼び出しごとに「pair <char、int>」を何度も記述する必要があります。 タイプが単純な場合、テンプレートにテンプレートがあり、テンプレートドライブがある場合はどうでしょうか。 ここでは、自動コンストラクターが役立ちます。

 myMap.insert(auto('a', 10)); 

関数、コンストラクタ、または自動演算子は、それが何であれ、挿入メソッドの入力パラメーターの記述に適合する何らかの種類のオブジェクトを作成します。

しかし、残念ながら、C ++にはまだオブジェクトを作成するためのそのような方法論がありませんが、いつかそれが現れることを望みますが、今のところ、そのようなタスクの実装のバージョンを提示したいと思います。 もちろん、主な目標はコードの記述を簡素化することですが、同様に重要なタスクはプログラムに害を及ぼさないことです。ボリュームを増やしたり、実行を遅くしたりしないでください。 理想的には、自動コンストラクタなしで記述するものと同一である必要があります。

など。 要求された型に変換できる何らかの種類のユニバーサルオブジェクトを作成し、コンパイル段階で実行する必要があります。 もちろん、コンパイルO0、Ogなどの最適化は考慮していません。 Osの最適化を行います。

オブジェクト(コンテナ)はすべての入力引数を受け入れ、ホームに保持し、要求された型に変換するときに、一度渡されたすべてを転送してコンストラクターを呼び出そうとします。

まず、オブジェクトのコピーとポインターおよびリンクの両方を保存できるユニバーサル変数が必要です。 この場合、ポインターとコピーは同じですが、リンクはより複雑です。

 template<typename T> struct Value { constexpr Value(T v): v(v) {} constexpr T get() {return v;} T v; }; template<typename T> struct Value<T&> { constexpr Value(T& v): v(&v) {} constexpr T& get() {return *v;} T* v; }; 

ここではすべてが比較的単純であり、引数を受け入れてそのコピーを保存し、リンクの場合はポインターに変換し、その逆も行います。

特殊性自体は、普遍性を作成するために参照によって引数を取り除くために必要ですが、ポインターへの変換なしでは、コンパイラーはコンパイル段階での実行を拒否しますが、違いのように見えますが、ポインターまたはリンクに格納します。

ここで、任意の型の引数の数に制限のないユニバーサルコンテナを作成する必要があります。

 template<typename... Types> struct Container { constexpr Container() {} template<typename T> constexpr operator T() {return get<T>();} template<typename T, typename... Values> T constexpr get(Values&&... v) {return T((Values&&)v...);} }; template<typename Type, typename... Types> struct Container<Type, Types...> { constexpr Container(const Type&& arg, const Types&&... args): arg(arg), args((Types&&)args...) {} template<typename T> constexpr operator T() {return get<T>();} template<typename T, typename... Values> T constexpr get(Values&&... v) {return args.get<T>((Values&&)v..., arg.get());} Value<Type> arg; Container<Types...> args; }; 

再帰コンテナは、さまざまなタイプの引数を無制限に受け入れ、最初の引数をそれ自体に入れ、残りを最後の引数に到達するまで、残りの引数とともにネストされたコンテナに入れます。

また、このコンテナには、使用可能なすべての引数をネストして再帰的なgetメソッドを呼び出すことで必要な任意の型への変換演算子があります。

すべての引数は、参照を失わないように、右辺値引数として最終宛先値に渡されます。

そして最後に、ユニバーサルAutoコンストラクター自体。 大文字で呼んだ autoキーワードは既に使用されています。 そして、この機能がデザイナーとして機能することを考えると、大文字は彼女の顔にあります。

 template<typename... Types> constexpr Container<Types...> Auto(Types&&... args) {return Container<Types...>((Types&&)args...);} 

最後に、ValueクラスをContainerクラスのプライベートエリアに移動して、次を取得します。

 template<typename... Types> struct Container { constexpr Container() {} template<typename T> constexpr operator T() {return get<T>();} template<typename T, typename... Values> T constexpr get(Values&&... v) {return T((Values&&)v...);} }; template<typename Type, typename... Types> struct Container<Type, Types...> { constexpr Container(const Type&& arg, const Types&&... args): arg(arg), args((Types&&)args...) {} template<typename T> constexpr operator T() {return get<T>();} template<typename T, typename... Values> T constexpr get(Values&&... v) {return args.get<T>((Values&&)v..., arg.get());} private: template<typename T> struct Value { constexpr Value(T v): v(v) {} constexpr T get() {return v;} T v; }; template<typename T> struct Value<T&> { constexpr Value(T& v): v(&v) {} constexpr T& get() {return *v;} T* v; }; Value<Type> arg; Container<Types...> args; }; template<typename... Types> constexpr Container<Types...> Auto(Types&&... args) {return Container<Types...>((Types&&)args...);} 

すべての変換はコンパイル段階で実行され、コンストラクターの直接呼び出しと変わりません。

確かに、ささいな不都合があります-入力引数のオプションを提供できるプロンプターは1つではありません。

Source: https://habr.com/ru/post/J316030/


All Articles