質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

4回答

4730閲覧

std::forwardについて

Y.R.T

総合スコア42

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2020/08/06 04:58

下記構文で、std::forwardをする意味は何でしょうか?

template <typename Time = std::chrono::microseconds, typename Clock = std::chrono::high_resolution_clock> struct perf_timer { template <typename F, typename... Args> static Time duration(F&& f, Args... args) { auto start = Clock::now(); std::invoke(std::forward<F>(f), std::forward<Args>(args)...); auto end = Clock::now(); return std::chrono::duration_cast<Time>(end - start); } };

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

関数テンプレートの引数に現れる && はユニバーサル参照といわれるものです。 実引数によって右辺値参照にも左辺値参照にもなります。 つまり、左辺値にも右辺値にも対応した関数になるというわけです。

しかし、「右辺値参照は左辺値」という性質があるので std::forward を介さずにそのまま std::invoke に渡したら常に左辺値参照として受け取られてしまいます。 std::forward は右辺値参照の場合は右辺値に、左辺値参照の場合は左辺値にキャストしなおす機能です。 duration 関数が受け取ったときの左辺値・右辺値の区別を伝播させるために std::forward を使うのです。

が、この場合に args は参照で受け取っていないので std::forward を適用した結果は常に右辺値参照になります。 std::move で書いてもたぶん同じじゃないですかね。

投稿2020/08/06 05:42

SaitoAtsushi

総合スコア5684

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yohhoy

2020/08/06 05:45

Args&&... ではなく Args... となっていることに今気づきました!
Y.R.T

2020/08/06 05:59

構文の std::invoke(std::forward<F>(f), std::forward<Args>(args)...); が無かったらどのような影響がでますか?
guest

0

ベストアンサー

こんにちは。

テンプレート引数Fを使った F&& f はユニバーサル参照と言って、右辺値と左辺値の両方を受け取れます。(普通の関数で例えばint&& xの時は右辺値だけなのですが、テンプレートの場合は異なります。)

さて、超頭が痛くなりそうな話ですが、右辺値参照型の変数は式の左辺におけます(代入できる)から「左辺値」です。(右辺値には代入できません。当たり前です。1=2;などとできるわけないですね。)
ですので、duration()のfが右辺値参照だった場合でも、std::invoke(f, 略);と渡すとfは左辺値として渡されてしまいます。

しかし、std::invokeが右辺値参照を受け取れるなら、fが右辺値参照だったら右辺値参照として、fが左辺値参照だったら左辺値として渡したいです。それを行うのが std::forwardです。

↓昔書いた解説です。十分に分かりやすいとまでは言えないのですが、多少は役に立つかもです。
2.ところで右辺値参照は左辺値です
2.std::forwardで左辺値と右辺値を中継


右辺値参照は実は左辺値とか言われると右辺値参照とか意味ないじゃんって感じるかも知れません。
でもこれは関数オーバーロードの使い方を広げる便利な仕組みです。
コピーとムーブを明確に分けて記述でき、しかも呼び出し側が右辺値(≒一時変数)ならムーブしてよいことが自明なので自動的にムーブが呼ばれるという便利な仕組みです。

投稿2020/08/06 06:01

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Y.R.T

2020/08/06 06:09

皆さんご丁寧に教えていただき、ありがとうございます。 大変勉強になりました。
guest

0

std::forwardをする意味は何でしょうか

ムーブ操作のみサポートする型(たとえばstd::unique_ptr)を扱うために、std::forwardによる完全転送が必要となります。
訂正:オリジナルコードでは Args&&... ではなく Args... となっているため、std::forward<Args>(args)... は完全転送とは異なる動きになりますね。(第一引数F&&
std::forward<F>(f)で完全転送)

C++

1void f(std::unique_ptr<int> up) 2{ 3 std::cout << "f: *up=" << *up << "\n"; 4} 5 6int main() 7{ 8 auto up = std::unique_ptr<int>{new int(100)}; 9 auto d = perf_timer<>::duration( f, std::move(up) ); 10 std::cout << "elapsed=" << d.count() << " [usec]"; 11}

https://wandbox.org/permlink/483mW5YcRxBwjlix

投稿2020/08/06 05:25

編集2020/08/06 06:33
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

forwardは右辺値参照にキャストするんだから、「無駄なコピーを抑止する」ためじゃないかしら。

投稿2020/08/06 05:06

episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Y.R.T

2020/08/06 05:34

ありがとうございます。 戻り値を参照にしないとコピー(あるいはムーブ)が走るから、という理解でいいでしょうか? https://qiita.com/rinse_/items/cffa87016b7de49391ae で右辺値・左辺値を調べたのですが、この内容は 覚える必要はありますか?
episteme

2020/08/06 05:40

void g(T x); void f(T x) { g(x); } なんてとき、g()にxを引き渡すときにコピーが発生する。 コピーせずにそのまま引き渡したいがためforwardする。 # yohhoyさん指摘のように、そもそもコピーできないから  そのまま引き渡したいがためのforwardもある。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問