前提
ムーブコンストラクタが動いた時標準出力に[ムーブ]と出力
コピーコンストラクタが動いた時標準出力に[コピー]と出力
するような構造体Hogeと
ユニバーサル参照とstd::forwardを使い
オーバーロードなしの同じ1つの関数で
右辺値が代入されたら引数から内部の変数へムーブし
左辺値が代入されたら引数から内部の変数へコピー
するような関数funcを作っていた時理解できない問題が発生したので
質問させていただきます。
ちなみに関数funcはenable_ifを使うことで
構造体Hoge以外の型を受け付けないようになってます。
下記のコードはC++17でコンパイルしています。
質問内容
質問は2つございます。
質問1:
関数func中の
c++
1Hoge hoge; 2hoge = std::forward<Type>(arg);
の部分を
c++
1Type hoge = std::forward<Type>(arg);
と書いてしまうと下記の結果
(#define PROBLEM_OCCURRED_1を有効にした場合の実行結果)のようになります。
なぜ左辺値代入時コピーが行われず右辺値代入時もコピーが行われてしまうのでしょうか。
質問2:
また質問①のようにfunc内で
c++
1Hoge hoge = std::forward<Type>(arg);
と書いてしまうと
(#define PROBLEM_OCCURRED_2を有効にした場合の実行結果)のようになります
なぜ右辺値代入時ムーブが行われずコピーがが行われてしまうのでしょうか。
ご回答どうかよろしくお願いします。
備考
上記の問題を発生させたいのならば下記のコード中
4行目のPROBLEM_OCCURRED_1(1つ目の問題を発生させる)か
5行目のPROBLEM_OCCURRED_2(2つ目の問題を発生させる)の
コメントアウトを外してもらえば発生させられます。
問題のコード
c++
1#include <iostream> 2#include <type_traits> 3 4//#define PROBLEM_OCCURRED_1//質問①の問題発生させるスイッチ 5//#define PROBLEM_OCCURRED_2//質問②の問題発生させるスイッチ 6 7// 8// 構造体Hogeのプロトタイプ宣言 9// 10struct Hoge; 11 12// 13// Hoge判定用メタ関数 14// 15template<class Type> struct is_hoge:std::false_type{}; 16template<> struct is_hoge<Hoge&>:std::true_type{}; 17template<> struct is_hoge<const Hoge&>:std::true_type{}; 18template<> struct is_hoge<Hoge&&>:std::true_type{}; 19template<> struct is_hoge<Hoge>:std::true_type{}; 20 21// 22// 構造体Hogeの定義 23// 24struct Hoge{ 25 Hoge()noexcept = default; 26 ~Hoge() = default; 27 Hoge& operator=(const Hoge& hoge)noexcept{std::cout << "コピー" << std::endl; return *this;} 28 Hoge& operator=(Hoge&& hoge)noexcept{std::cout << "ムーブ" << std::endl; return *this;} 29 Hoge(const Hoge& hoge)noexcept{*this = hoge;} 30 Hoge(Hoge&& hoge)noexcept{*this = hoge;} 31}; 32 33// 34// 内部で確保したHogeのオブジェクトへコピーorムーブする関数 35// 引数はユニバーサル参照となっており右辺値でも左辺値でも入力可能 36// enable_ifを使うことでHoge以外代入できないようになっている 37// 38template<class Type> 39typename std::enable_if<is_hoge<Type>::value>::type func(Type&& arg)noexcept{ 40#if defined(PROBLEM_OCCURRED_1) 41 Type hoge = std::forward<Type>(arg);//質問①:エラーは起こらないが左辺値代入時コピーが行われず右辺値代入時もコピーが行われてしまう 42 43#elif defined(PROBLEM_OCCURRED_2) 44 Hoge hoge = std::forward<Type>(arg);//質問②:エラーは起こらないが右辺値代入時ムーブが行われずコピーがが行われる 45 46#else 47 Hoge hoge; 48 hoge = std::forward<Type>(arg);//引数が左辺値ならコピー,右辺値ならムーブになる 49 50#endif 51 52} 53 54// 55// メイン関数 56// 57int main(void) 58{ 59 std::cout << "/////////////////////////////////////////////" << std::endl; 60 std::cout << "左辺値代入" << std::endl; 61 Hoge lr; 62 func(lr); 63 64 std::cout << "/////////////////////////////////////////////" << std::endl; 65 std::cout << "右辺値代入" << std::endl; 66 func(Hoge()); 67 68 return 0; 69} 70
問題を発生させなかった場合の実行結果
terminal
1///////////////////////////////////////////// 2左辺値代入 3コピー 4///////////////////////////////////////////// 5右辺値代入 6ムーブ
ちゃんと左辺値代入でも右辺値代入正しい挙動をしています。
#define PROBLEM_OCCURRED_1を有効にした場合の実行結果
terminal
1///////////////////////////////////////////// 2左辺値代入 3///////////////////////////////////////////// 4右辺値代入 5コピー
左辺値代入時コピーが行われず右辺値代入時もコピーが行われてしまっている。
#define PROBLEM_OCCURRED_2を有効にした場合の実行結果
terminal
1///////////////////////////////////////////// 2左辺値代入 3コピー 4///////////////////////////////////////////// 5右辺値代入 6コピー
右辺値代入時ムーブが行われずコピーがが行われてしまっている。
開発環境の備考
ツールの種類 | ツールの名前 | バージョン |
---|---|---|
コンパイラ | clang++ | 6.0.0 |
OS | Linux Mint | 18.3 |
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2020/07/05 07:35