まあそもそもcout_check
の中身両方左辺値って書いてますやん、というツッコミはさておき本題へ。
端的に言うと、ret
のreturn文をreturn std::forward<T>(val);
にすれば通ります。
C++でもっともお手軽に型を確認する手段である、[[deprecated]]
attributeをつけて確認してみましょう。
cpp
1#include <iostream>
2#include <vector>
3#include <functional>
4#include <algorithm>
5#include <tuple>
6#include <type_traits>
7
8template <typename T>
9[[deprecated]] decltype(auto) ret(T&& val) {
10 return std::forward<T>(val);
11 //return val;
12}
13
14template <typename T>
15auto cout_check(T&) {
16 std::cout << "左辺値" << std::endl;
17}
18
19template <typename T>
20auto cout_check(T&&) {
21 std::cout << "右辺値" << std::endl;
22}
23int f() { return 3; }
24
25int main()
26{
27 // T から T&&に変換できないとは?
28 int i = 0;
29 cout_check(ret(i));
30 cout_check(ret(0));
31 cout_check(ret(f()));
32}
https://wandbox.org/permlink/2bW402AZYXRdO3Qt
prog.cc:29:16: warning: 'ret<int &>' is deprecated [-Wdeprecated-declarations]
cout_check(ret(i));
^
prog.cc:9:3: note: 'ret<int &>' has been explicitly marked deprecated here
[[deprecated]] decltype(auto) ret(T&& val) {
^
prog.cc:30:16: warning: 'ret<int>' is deprecated [-Wdeprecated-declarations]
cout_check(ret(0));
^
prog.cc:9:3: note: 'ret<int>' has been explicitly marked deprecated here
[[deprecated]] decltype(auto) ret(T&& val) {
^
prog.cc:31:16: warning: 'ret<int>' is deprecated [-Wdeprecated-declarations]
cout_check(ret(f()));
^
prog.cc:9:3: note: 'ret<int>' has been explicitly marked deprecated here
[[deprecated]] decltype(auto) ret(T&& val) {
^
3 warnings generated.
左辺値
右辺値
右辺値
ret
にlvalueを渡したときは確かに引数の型はint&
になっていて、rvalueを渡したときはint&&
になっていることが確認できます。
じゃあなぜだめだったのか?もっとシンプルなコードを見ていきましょう。
cpp
1#include <type_traits>
2int main()
3{
4 int&& r = 3;
5 static_assert(std::is_same_v<decltype(r), int&&>);
6 decltype(auto) aa = r;
7}
https://wandbox.org/permlink/dNlWQiBpA0v6UxcJ
prog.cc:6:20: error: rvalue reference to type 'int' cannot bind to lvalue of type 'int'
decltype(auto) aa = r;
^ ~
1 error generated.
つまりどういうことかと言うと、int&&
型の変数はlvalueだと言うことです。
ではstd::forward
が入るとどうなるのか?std::forward
はstatic_cast<T&&>(t)
を返すのでした。
rvalue referenceへのキャスト式の評価結果はrvalueであり、rvalue referenceを返す関数の呼び出しの評価結果はrvalueであることを利用します。
順を追ってコードを書き換えていきましょう。
cpp
1#include <type_traits>
2int main()
3{
4 int&& r = 3;
5 static_assert(std::is_same_v<decltype(r), int&&>);
6 [[maybe_unused]] decltype(auto) aa = static_cast<int&&>(r);
7}
https://wandbox.org/permlink/vTpYLaRos85YbmOm
このようにするとコンパイルが通ります。なぜならばrvalue referenceへのキャスト式の評価結果はrvalueだからです。
cpp
1#include <type_traits>
2#include <utility>
3int main()
4{
5 int&& r = 3;
6 static_assert(std::is_same_v<decltype(r), int&&>);
7 [[maybe_unused]] decltype(auto) aa = std::forward<int>(r);
8}
https://wandbox.org/permlink/9NQcbjeMBFkf3rLY
このコードもコンパイルが通ります。なぜならばrvalue referenceを返す関数の呼び出しの評価結果はrvalueだからです。
もっと全体的に知りたい場合は
みんなlvalueとrvalueを難しく考えすぎちゃいないかい? - Qiita
をどうぞ。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/09 04:26