teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

m

2019/05/04 09:13

投稿

yumetodo
yumetodo

スコア5852

answer CHANGED
@@ -11,7 +11,7 @@
11
11
  このコードですがあきらかにmoveの誤用です。この場合単に
12
12
 
13
13
  ```cpp
14
- std::vector<int> twice_vec(const std::vector<int>& vec) {
14
+ std::vector<int> twice_vec(std::vector<int> vec) {
15
15
  for (auto& e : vec) {
16
16
  e *= 2;
17
17
  }
@@ -30,4 +30,81 @@
30
30
 
31
31
  move sematicsそのものについての理解が不十分なように思えるので
32
32
  [みんなlvalueとrvalueを難しく考えすぎちゃいないかい?](https://qiita.com/yumetodo/items/8eae5714a6cfe1c0407d)
33
- をお読みください。
33
+ をお読みください。
34
+
35
+ ---
36
+
37
+ 追記:
38
+
39
+ あーわかったわかった、他の人の解答見てて視点漏れしてたので解説し直し。
40
+
41
+ ```cpp
42
+ void f(C& c);
43
+ C g(const C& c);
44
+ ```
45
+
46
+ この2つのどちらを選ぶべきか、2つの用例を考えて比較します。
47
+
48
+ immutableにしたい、なにかを元にして新規に領域を確保するようなケースでは
49
+
50
+ ```cpp
51
+ void f(C& dest, const C& src);
52
+ C g(const C& c);
53
+ int main()
54
+ {
55
+ C src;
56
+ //do something
57
+ C dest;
58
+ f(src, dest);
59
+ }
60
+ ```
61
+
62
+
63
+
64
+ ```cpp
65
+ C src;
66
+ //do something
67
+ C dest = g(src);
68
+ ```
69
+
70
+ ではあきらかに後者を選ぶべきです。これは上で解説したようにNRVOが働くため2重copyにはならないから可読性の観点と、もし`f`/`g`の中で`C`のコンストラクタを呼んでいてそれを変更して返却するような場合ではコピー代入演算子のコスト分お得です。私の解答はここに主眼をおいていました。
71
+
72
+ mutableにできる場合はChironianさんの解答が該当ですね。
73
+
74
+ std::moveするのって
75
+
76
+ 1) 関数の引数に渡して所有権を放棄するとき
77
+
78
+ ex.) `std::vector::emplace_back`
79
+
80
+ ```cpp
81
+ f(std::move(a));
82
+ ```
83
+
84
+ この時関数の引数の型は
85
+
86
+ ```cpp
87
+ void f1(C c);
88
+ void f2(C&& c);//rvalue reference
89
+ template<typename CC>
90
+ void f3(CC&& c);//universal reference(forwarding reference)
91
+ ```
92
+
93
+ でないとmove semanticsできない(=所有権の放棄を識別できない)
94
+
95
+ `f1`の場合は関数呼び出し時点でmove ctor呼び出し。内部で再度moveするなら避けるべき
96
+
97
+ `f2`の場合は関数の内部でmove semanticsできる。
98
+
99
+ `f3`はどちらかというとparfect forwarding
100
+
101
+ 2) move ctorを呼び出す時
102
+
103
+ ```cpp
104
+ C c2 = std::move(c1);
105
+ ```
106
+
107
+ が大半な気がします。
108
+
109
+
110
+ いずれにせよ場面毎に設計上の制約を把握した上で適切に判断する必要があるのでなかなか難しいですね。追記したのにうまくまとまらないし。

2

RVO

2019/05/04 09:13

投稿

yumetodo
yumetodo

スコア5852

answer CHANGED
@@ -24,6 +24,8 @@
24
24
 
25
25
  NRVOが働かなかった時代においては、確かに引数経由で返却する習慣がありましたが、今では代入演算子の分コスト的に不利です。何も考えずにそのまま戻り値で返しましょう。
26
26
 
27
+ ちなみにC++17以降ではRVOが義務化され、RVOになる場合copy/move ctorが削除されていても戻り値として返却できます。
28
+
27
29
  ---
28
30
 
29
31
  move sematicsそのものについての理解が不十分なように思えるので

1

clang

2019/05/04 04:50

投稿

yumetodo
yumetodo

スコア5852

answer CHANGED
@@ -20,7 +20,7 @@
20
20
  }
21
21
  ```
22
22
 
23
- とすればよいです。なぜならば戻り値で`std::move`をわざわざ書くと、コンパイラによるNRVOを阻害して動作を遅くするからです。`return vec`とただ書けば、NRVOによってコストは0です。
23
+ とすればよいです。なぜならば戻り値で`std::move`をわざわざ書くと、コンパイラによるNRVOを阻害して動作を遅くするからです。`return vec`とただ書けば、NRVOによってコストは0です。(ついでにいうと、clangはstd::moveつけんな、と警告を出します。)
24
24
 
25
25
  NRVOが働かなかった時代においては、確かに引数経由で返却する習慣がありましたが、今では代入演算子の分コスト的に不利です。何も考えずにそのまま戻り値で返しましょう。
26
26