MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
C++の基礎 - ユニバーサル参照のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
C++の基礎 - ユニバーサル参照
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == ユニバーサル参照 (universal reference) は、C++11で導入された特殊な参照型であり、<u>転送参照</u> (forwarding reference) とも呼ばれる。<br> テンプレート引数の型推論と組み合わせることで、左辺値と右辺値の両方を受け取ることができる。<br> <br> ユニバーサル参照は、C++の高度な機能であり、以下に示す事柄を理解する必要がある。<br> * 型推論が発生する場合の <code>T&&</code> のみがユニバーサル参照である。 * 参照の折り畳み規則により、左辺値と右辺値を区別できる。 * <code>std::forward</code> と組み合わせて完全転送を実現する。 * 名前付きの右辺値参照は左辺値として扱われる。 <br> 適切に使用することにより、柔軟なテンプレートコードを記述することができる。<br> <br><br> == ユニバーサル参照 == ==== ユニバーサル参照の概念 ==== ユニバーサル参照は、T&&という記法で表現されるが、右辺値参照とは異なる動作をする。<br> <br> 以下に示す2つの条件を満たす場合にのみ、ユニバーサル参照となる。<br> * 型推論が発生する。(auto または テンプレートパラメータ) * <code>T&&</code> の形式である。(cvqualifier や 他の修飾なし) <br> ==== ユニバーサル参照になる例 ==== <syntaxhighlight lang="c++"> // 1. autoでの型推論 auto&& var = expression; // 2. テンプレート関数の引数 template<typename T> void func(T&& param); // 3. テンプレートクラスのメンバ関数 (メンバ関数自体がテンプレート) template<typename T> class Widget { public: template<typename U> void process(U&& param); // ユニバーサル参照 }; </syntaxhighlight> <br> ==== ユニバーサル参照にならない例 ==== <syntaxhighlight lang="c++"> // 右辺値参照 (ユニバーサル参照ではない) の例 // 1. 型が確定している void func(std::string&& param); // 右辺値参照 // 2. constが付いている template<typename T> void func(const T&& param); // const右辺値参照 // 3. テンプレートクラスのメンバ関数 (クラスのテンプレートパラメータ) template<typename T> class Widget { public: void process(T&& param); // 右辺値参照 (型推論が発生しない) }; // 4. std::vectorの例 template<typename T> class vector { public: void push_back(T&& value); // 右辺値参照 (Tは既に確定している) }; </syntaxhighlight> <br><br> == 参照の折り畳み規則 == ==== 折り畳みルール ==== ユニバーサル参照の動作を理解するには、参照の折り畳み規則を知る必要がある。<br> <br> C++では、参照の参照は以下に示すルールで1つの参照に折り畳まれる。<br> <u>右辺値参照同士の組み合わせのみが右辺値参照となり、それ以外は左辺値参照になる。</u><br> * T& & → T& * T& && → T& * T&& & → T& * T&& && → T&& <br> ==== 型推論との組み合わせ ==== <syntaxhighlight lang="c++"> template<typename T> void func(T&& param); int x = 10; // 左辺値を渡した場合 func(x); // Tはint&と推論される // int& && → int& (参照の折り畳み) // paramの型はint& // 右辺値を渡した場合 func(10); // Tはintと推論される // int&& がそのまま使われる // paramの型はint&& </syntaxhighlight> <br><br> == ユニバーサル参照の基本的な使用 == ==== autoとの組み合わせ ==== <syntaxhighlight lang="c++"> #include <iostream> #include <string> int main() { int x = 10; const int cx = 20; // 左辺値を束縛 auto&& uref1 = x; // int&と推論される auto&& uref2 = cx; // const int&と推論される // 右辺値を束縛 auto&& uref3 = 30; // int&&と推論される auto&& uref4 = std::string("hello"); // std::string&&と推論される // 型の確認 (コンパイル時エラーを利用) // uref1の型を確認するには、以下をコメント解除 // decltype(uref1)* ptr = nullptr; std::cout << "uref1: " << uref1 << std::endl; // 10 std::cout << "uref3: " << uref3 << std::endl; // 30 return 0; } </syntaxhighlight> <br> ==== テンプレート関数での使用 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <string> template<typename T> void identify(T&& param) { std::cout << "Parameter received" << std::endl; } void testIdentify() { std::string str = "lvalue"; const std::string cstr = "const lvalue"; identify(str); // T = std::string&, param = std::string& identify(cstr); // T = const std::string&, param = const std::string& identify(std::string("rvalue"));// T = std::string, param = std::string&& identify("temp"); // T = const char (&)[5], param = const char (&)[5] } </syntaxhighlight> <br><br> == 詳細な型推論の例 == ==== その他のケース ==== <syntaxhighlight lang="c++"> #include <iostream> #include <type_traits> template<typename T> void analyzeType(T&& param) { std::cout << "--- Type Analysis ---" << std::endl; if (std::is_lvalue_reference<T>::value) { std::cout << "T is lvalue reference" << std::endl; } else if (std::is_rvalue_reference<T>::value) { std::cout << "T is rvalue reference" << std::endl; } else { std::cout << "T is not a reference" << std::endl; } if (std::is_lvalue_reference<decltype(param)>::value) { std::cout << "param is lvalue reference" << std::endl; } else if (std::is_rvalue_reference<decltype(param)>::value) { std::cout << "param is rvalue reference" << std::endl; } std::cout << std::endl; } int main() { int x = 10; int& lref = x; int&& rref = 20; analyzeType(x); // T = int&, param = int& analyzeType(lref); // T = int&, param = int& analyzeType(rref); // T = int&, param = int& (rrefは左辺値) analyzeType(10); // T = int, param = int&& analyzeType(std::move(x)); // T = int, param = int&& return 0; } </syntaxhighlight> <br> ==== const修飾の扱い ==== <syntaxhighlight lang="c++"> #include <iostream> template<typename T> void func(T&& param) { // 型情報の表示用 } int main() { int x = 10; const int cx = 20; func(x); // T = int&, param = int& func(cx); // T = const int&, param = const int& func(10); // T = int, param = int&& const int& clref = x; func(clref); // T = const int&, param = const int& return 0; } </syntaxhighlight> <br><br> == ユニバーサル参照の例 == ==== ファクトリ関数 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <memory> #include <string> class Widget { private: std::string name; int value; public: Widget(std::string n, int v) : name(std::move(n)), value(v) { std::cout << "Widget constructed: " << name << std::endl; } void display() const { std::cout << "Widget: " << name << ", Value: " << value << std::endl; } }; // ユニバーサル参照を使用したファクトリ関数 template<typename T, typename... Args> std::unique_ptr<T> make_unique_custom(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } int main() { std::string name = "Widget1"; // 左辺値を渡す auto w1 = make_unique_custom<Widget>(name, 100); // 右辺値を渡す auto w2 = make_unique_custom<Widget>(std::string("Widget2"), 200); // 混在 auto w3 = make_unique_custom<Widget>(name, 300); w1->display(); w2->display(); w3->display(); return 0; } </syntaxhighlight> <br> ==== ラッパー関数 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <chrono> #include <string> // 処理対象の関数 void process(std::string& str) { str += " (processed)"; std::cout << "Processing lvalue: " << str << std::endl; } void process(std::string&& str) { str += " (processed as rvalue)"; std::cout << "Processing rvalue: " << str << std::endl; } // ユニバーサル参照を使用したラッパー関数 template<typename T> void logAndProcess(T&& param) { auto start = std::chrono::high_resolution_clock::now(); std::cout << "Logging before processing..." << std::endl; process(std::forward<T>(param)); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double, std::milli> elapsed = end - start; std::cout << "Elapsed time: " << elapsed.count() << " ms" << std::endl; } int main() { std::string lvalue = "Left"; logAndProcess(lvalue); // 左辺値として転送 logAndProcess(std::string("Right")); // 右辺値として転送 logAndProcess(std::move(lvalue)); // 右辺値として転送 return 0; } </syntaxhighlight> <br> ==== 範囲forループとの組み合わせ ==== <syntaxhighlight lang="c++"> #include <iostream> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // ユニバーサル参照を使用 // 要素が左辺値なので、auto&&はint&として推論される for (auto&& num : numbers) { num *= 2; // 元のベクタの要素を変更 } for (const auto& num : numbers) { std::cout << num << " "; // 2 4 6 8 10 } std::cout << std::endl; return 0; } </syntaxhighlight> <br><br> == 完全転送との関係 == ユニバーサル参照は、完全転送 (perfect forwarding) を実現するための基礎となる。<br> <br> <code>std::forward</code> と組み合わせることにより、引数の値カテゴリを保持したまま別の関数に転送できる。<br> <br> 詳細は、[[C++の基礎 - 完全転送|C++の基礎 - 完全転送のページ]]を参照すること。<br> <br><br> == その他 : ユニバーサル参照の注意 == ==== 名前付きの右辺値参照は左辺値 ==== この問題を解決するには、std::forwardを使用する必要がある。<br> <br> <syntaxhighlight lang="c++"> #include <iostream> void process(int& x) { std::cout << "Lvalue reference version" << std::endl; } void process(int&& x) { std::cout << "Rvalue reference version" << std::endl; } template<typename T> void wrapper(T&& param) { // paramは名前を持つため、左辺値である process(param); // 常に左辺値参照が呼ばれる } int main() { wrapper(10); // 右辺値を渡しても、左辺値参照が呼ばれる int x = 20; wrapper(x); // 左辺値参照が呼ばれる return 0; } </syntaxhighlight> <br> ==== cv修飾との組み合わせはユニバーサル参照ではない ==== <syntaxhighlight lang="c++"> // ユニバーサル参照ではない例 template<typename T> void func1(const T&& param); // const右辺値参照 template<typename T> void func2(volatile T&& param); // volatile右辺値参照 template<typename T> void func3(T* && param); // ポインタの右辺値参照 </syntaxhighlight> <br> ==== std::vectorとの違い ==== <syntaxhighlight lang="c++"> #include <vector> template<typename T> class MyVector { public: // これはユニバーサル参照ではない // Tは既にMyVectorのインスタンス化時に確定しているため void push_back(T&& value); }; // 使用例 MyVector<int> vec; int x = 10; // vec.push_back(x); // エラー : 左辺値を渡せない vec.push_back(20); // OK: 右辺値 vec.push_back(std::move(x)); // OK: 右辺値にキャスト </syntaxhighlight> <br><br> == 型推論のデバッグ == ==== コンパイル時の型確認 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <typeinfo> #include <type_traits> template<typename T> void printType(T&& param) { std::cout << "Type of T: " << typeid(T).name() << std::endl; std::cout << "Type of param: " << typeid(param).name() << std::endl; std::cout << "Is T lvalue reference: " << std::is_lvalue_reference<T>::value << std::endl; std::cout << "Is T rvalue reference: " << std::is_rvalue_reference<T>::value << std::endl; std::cout << "Is param lvalue reference: " << std::is_lvalue_reference<decltype(param)>::value << std::endl; std::cout << "Is param rvalue reference: " << std::is_rvalue_reference<decltype(param)>::value << std::endl; std::cout << "---" << std::endl; } int main() { int x = 10; const int cx = 20; printType(x); // T = int& printType(cx); // T = const int& printType(10); // T = int printType(std::move(x)); // T = int return 0; } </syntaxhighlight> <br> ==== 意図的なコンパイルエラーを利用 ==== <syntaxhighlight lang="c++"> template<typename T> void func(T&& param) { // 型を確認するために意図的にエラーを起こす // typename T::NonExistentType error; } int main() { int x = 10; func(x); // コンパイルエラーのメッセージにTの型が表示される return 0; } </syntaxhighlight> <br><br> == パフォーマンスへの影響 == ユニバーサル参照を使用することにより、以下に示すメリットがある。<br> ただし、誤った使用は逆効果となる可能性があるため、std::forwardとの組み合わせが重要である。<br> <br> * 不要なコピーを避けられる。 * 左辺値と右辺値の両方を効率的に処理できる。 * テンプレートコードの柔軟性が向上する。 * 完全転送により、パフォーマンスを損なわずに引数を転送できる。 <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:C++]]
C++の基礎 - ユニバーサル参照
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse