回答編集履歴

3

m

2019/05/04 09:13

投稿

yumetodo
yumetodo

スコア5850

test CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  ```cpp
26
26
 
27
- std::vector<int> twice_vec(const std::vector<int>& vec) {
27
+ std::vector<int> twice_vec(std::vector<int> vec) {
28
28
 
29
29
  for (auto& e : vec) {
30
30
 
@@ -63,3 +63,157 @@
63
63
  [みんなlvalueとrvalueを難しく考えすぎちゃいないかい?](https://qiita.com/yumetodo/items/8eae5714a6cfe1c0407d)
64
64
 
65
65
  をお読みください。
66
+
67
+
68
+
69
+ ---
70
+
71
+
72
+
73
+ 追記:
74
+
75
+
76
+
77
+ あーわかったわかった、他の人の解答見てて視点漏れしてたので解説し直し。
78
+
79
+
80
+
81
+ ```cpp
82
+
83
+ void f(C& c);
84
+
85
+ C g(const C& c);
86
+
87
+ ```
88
+
89
+
90
+
91
+ この2つのどちらを選ぶべきか、2つの用例を考えて比較します。
92
+
93
+
94
+
95
+ immutableにしたい、なにかを元にして新規に領域を確保するようなケースでは
96
+
97
+
98
+
99
+ ```cpp
100
+
101
+ void f(C& dest, const C& src);
102
+
103
+ C g(const C& c);
104
+
105
+ int main()
106
+
107
+ {
108
+
109
+ C src;
110
+
111
+ //do something
112
+
113
+ C dest;
114
+
115
+ f(src, dest);
116
+
117
+ }
118
+
119
+ ```
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+ ```cpp
128
+
129
+ C src;
130
+
131
+ //do something
132
+
133
+ C dest = g(src);
134
+
135
+ ```
136
+
137
+
138
+
139
+ ではあきらかに後者を選ぶべきです。これは上で解説したようにNRVOが働くため2重copyにはならないから可読性の観点と、もし`f`/`g`の中で`C`のコンストラクタを呼んでいてそれを変更して返却するような場合ではコピー代入演算子のコスト分お得です。私の解答はここに主眼をおいていました。
140
+
141
+
142
+
143
+ mutableにできる場合はChironianさんの解答が該当ですね。
144
+
145
+
146
+
147
+ std::moveするのって
148
+
149
+
150
+
151
+ 1) 関数の引数に渡して所有権を放棄するとき
152
+
153
+
154
+
155
+ ex.) `std::vector::emplace_back`
156
+
157
+
158
+
159
+ ```cpp
160
+
161
+ f(std::move(a));
162
+
163
+ ```
164
+
165
+
166
+
167
+ この時関数の引数の型は
168
+
169
+
170
+
171
+ ```cpp
172
+
173
+ void f1(C c);
174
+
175
+ void f2(C&& c);//rvalue reference
176
+
177
+ template<typename CC>
178
+
179
+ void f3(CC&& c);//universal reference(forwarding reference)
180
+
181
+ ```
182
+
183
+
184
+
185
+ でないとmove semanticsできない(=所有権の放棄を識別できない)
186
+
187
+
188
+
189
+ `f1`の場合は関数呼び出し時点でmove ctor呼び出し。内部で再度moveするなら避けるべき
190
+
191
+
192
+
193
+ `f2`の場合は関数の内部でmove semanticsできる。
194
+
195
+
196
+
197
+ `f3`はどちらかというとparfect forwarding
198
+
199
+
200
+
201
+ 2) move ctorを呼び出す時
202
+
203
+
204
+
205
+ ```cpp
206
+
207
+ C c2 = std::move(c1);
208
+
209
+ ```
210
+
211
+
212
+
213
+ が大半な気がします。
214
+
215
+
216
+
217
+
218
+
219
+ いずれにせよ場面毎に設計上の制約を把握した上で適切に判断する必要があるのでなかなか難しいですね。追記したのにうまくまとまらないし。

2

RVO

2019/05/04 09:13

投稿

yumetodo
yumetodo

スコア5850

test CHANGED
@@ -50,6 +50,10 @@
50
50
 
51
51
 
52
52
 
53
+ ちなみにC++17以降ではRVOが義務化され、RVOになる場合copy/move ctorが削除されていても戻り値として返却できます。
54
+
55
+
56
+
53
57
  ---
54
58
 
55
59
 

1

clang

2019/05/04 04:50

投稿

yumetodo
yumetodo

スコア5850

test CHANGED
@@ -42,7 +42,7 @@
42
42
 
43
43
 
44
44
 
45
- とすればよいです。なぜならば戻り値で`std::move`をわざわざ書くと、コンパイラによるNRVOを阻害して動作を遅くするからです。`return vec`とただ書けば、NRVOによってコストは0です。
45
+ とすればよいです。なぜならば戻り値で`std::move`をわざわざ書くと、コンパイラによるNRVOを阻害して動作を遅くするからです。`return vec`とただ書けば、NRVOによってコストは0です。(ついでにいうと、clangはstd::moveつけんな、と警告を出します。)
46
46
 
47
47
 
48
48