15
Effective Modern C++ 勉強会#6 Item 25 星野 喬(@starpoz) サイボウズ・ラボ 2015-06-17 1

Effective Modern C++ 勉強会#6 Item25

Embed Size (px)

Citation preview

Effective Modern C++勉強会#6 Item 25

星野喬(@starpoz)

サイボウズ・ラボ

2015-06-17

1

Item 25Use std::move on rvaluereferences, std::forward on universal references.

2

Rvalue reference で受けるとき

•渡された中身は move して良い

3

struct Widget {

std::string name;

std::shared_ptr<SomeDataStructure> p;

Widget(Widget&& rhs)

: name(std::move(rhs.name))

, p(std::move(rhs.p)) {

}

};

Universal reference で受けるとき

•渡された中身は forward する

4

struct Widget {

std::string name;

template <typename T>

void setName(T&& newName) {

name = std::forward<T>(newName);

}

};

Rvalue reference に std::forward を使うのはやめた方が良い

•動く、けれど

•冗長で

•間違いやすく

•自然ではない

5

Universal reference を std::move するのはやめた方が良い

6

struct Widget {

std::string name;

template <typename T>

void setName(T&& newName) {

name = std::move(newName);

}

};

std::string getWidgetName() {

//...

}

int main() {

Widget w;

auto n = getWidgetName();

w.setName(n);

// n is unknown now.

}

Overload する手段がある

• const lvalue reference と rvalue reference

•欠点• コードが増える

• 効率が悪くなる(ことがある)

• 引数の数が増えると爆発する (2^n)

7

struct Widget {

void setName(const std::string& newName) {

name = newName;

}

void setName(std::string&& newName) {

name = std::move(newName);

}

}

w.setName("Adala Novak");

可変長引数の場合

• Universal reference にせざるをえない

8

template <class T, class... Args>

shared_ptr<T> make_shared(Args&&... args);

template <class T, class... Args>

unique_ptr<T> make_unique(Args&&... args);

std::move や std::forward は最後に 1 回のみ使う

9

template <typename T>

void setSignText(T&& text) {

sign.setText(text);

auto now = std::chrono::system_clock::now();

signHistory.add(now, std::forward<T>(text));

}

std::move_if_noexcept

•例外が飛ぶ可能性のある関数に渡すときはmove しない

10

// cppreference

template<class T>

constexpr typename std::conditional<

!std::is_nothrow_move_constructible<T>::value &&

std::is_copy_constructible<T>::value,

const T&,

T&&

>::type move_if_noexcept(T& x);

Rvalue reference を値返しするとき• std::move() を使って return すると move をサポートしていない場合に copy constructor でコピーされる

• std::move() 使わないと強制コピー

11

Matrix operator+(Matrix&& lhs, const Matrix& rhs)

{

lhs += rhs;

return std::move(lhs);

}

Matrix operator+(Matrix&& lhs, const Matrix& rhs)

{

lhs += rhs;

return lhs; // copy

}

Universal reference を値返しするとき•同様に std::forward を使う

•使わないと強制コピー

12

template <typename T>

Fraction reduceAndCopy(T&& frac) {

frac.reduce();

return std::forward<T>(frac);

}

Local object を値返しするとき

• RVO (return value optimization) される or

• Local object を return するとき rvalue として扱われる

13

Widget makeWidget() {

Widget w;

// ...

return w;

}

Widget makeWidget() {

Widget w;

// ...

return std::move(w); // don't do this!

}

値渡しされた変数を値返しするとき• RVO はされない

• rvalue reference にはなる

14

Widget makeWidget(Widget w) {

return w;

}

Item25 Things to remember

• rvalue reference には std::move を、universalreference には std::forward を使う。ただし、最後に使われるときに。

•値返しの関数で rvalue reference と universal reference の扱いは同様。

• std::move と std::forward を local objects をreturn するのに使ってはいけない。使わなければ RVO 対象になる

15