回答編集履歴
3
m
answer
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
このコードですがあきらかにmoveの誤用です。この場合単に
|
12
12
|
|
13
13
|
```cpp
|
14
|
-
std::vector<int> twice_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
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
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
|
|