std:: forward_like
|
ヘッダーで定義
<utility>
|
||
|
template
<
class
T,
class
U
>
constexpr auto && forward_like ( U && x ) noexcept ; |
(C++23以降) | |
x
への参照を返します。これは
T&&
と同様の特性を持ちます。
戻り値の型は以下のように決定されます:
- std:: remove_reference_t < T > がconst修飾型の場合、戻り値型の参照型は const std:: remove_reference_t < U > となります。それ以外の場合、参照型は std:: remove_reference_t < U > となります。
-
T&&が左辺値参照型の場合、戻り値型も左辺値参照型となります。それ以外の場合、戻り値型は右辺値参照型となります。
T
が
referenceable type
でない場合、プログラムは不適格(ill-formed)です。
目次 |
パラメータ
| x | - |
型
T
のように転送される必要がある値
|
戻り値
上記のように決定された型の x への参照。
注記
std::forward
や
std::move
、
std::as_const
と同様に、
std::forward_like
は式の
値カテゴリ
にのみ影響を与える、またはconst修飾を追加する型キャストです。
m
が実際のメンバーであり、したがって
o.
m
が有効な式である場合、これは通常C++20コードでは
std::
forward
<
decltype
(
o
)
>
(
o
)
.
m
と記述されます。
これにより、 merge 、 tuple 、 language と呼ばれる3つのモデルが考えられます。
-
merge
:
const
修飾子を統合し、
Ownerの値カテゴリを採用する。 -
tuple
:
std
::
get
<
0
>
(
Owner
)
が行う動作(
Ownerが std:: tuple < Member > であると仮定)。 - language : std:: forward < decltype ( Owner ) > ( o ) . m が行う動作。
std::forward_like
が対象とする主なシナリオは、「遠い」オブジェクトの適応です。
tuple
シナリオも
language
シナリオも、この主なユースケースに対して適切な動作を行わないため、
std::forward_like
には
merge
モデルが使用されます。
| 機能テスト マクロ | 値 | 標準 | 機能 |
|---|---|---|---|
__cpp_lib_forward_like
|
202207L
|
(C++23) |
std::forward_like
|
実装例
template<class T, class U> constexpr auto&& forward_like(U&& x) noexcept { constexpr bool is_adding_const = std::is_const_v<std::remove_reference_t<T>>; if constexpr (std::is_lvalue_reference_v<T&&>) { if constexpr (is_adding_const) return std::as_const(x); else return static_cast<U&>(x); } else { if constexpr (is_adding_const) return std::move(std::as_const(x)); else return std::move(x); } } |
例
#include <cstddef> #include <iostream> #include <memory> #include <optional> #include <type_traits> #include <utility> #include <vector> struct TypeTeller { void operator()(this auto&& self) { using SelfType = decltype(self); using UnrefSelfType = std::remove_reference_t<SelfType>; if constexpr (std::is_lvalue_reference_v<SelfType>) { if constexpr (std::is_const_v<UnrefSelfType>) std::cout << "const lvalue\n"; else std::cout << "変更可能な左辺値\n"; } else { if constexpr (std::is_const_v<UnrefSelfType>) std::cout << "const rvalue\n"; else std::cout << "変更可能な右辺値\n"; } } }; struct FarStates { std::unique_ptr<TypeTeller> ptr; std::optional<TypeTeller> opt; std::vector<TypeTeller> container; auto&& from_opt(this auto&& self) { return std::forward_like<decltype(self)>(self.opt.value()); // std::forward<decltype(self)>(self).opt.value() を使用することは問題ありません。 // std::optionalが適切なアクセサを提供するため。 } auto&& operator[](this auto&& self, std::size_t i) { return std::forward_like<decltype(self)>(self.container.at(i)); // std::forward<decltype(self)>(self)[i] を使用することはあまり良くありません。なぜなら // コンテナは右辺値添字アクセスを提供しませんが、提供することは可能です。 } auto&& from_ptr(this auto&& self) { if (!self.ptr) throw std::bad_optional_access{}; return std::forward_like<decltype(self)>(*self.ptr); // *std::forward<decltype(self)>(self).ptr を使用することは適切ではありません。なぜなら // std::unique_ptr<TypeTeller> は常に非const左辺値にデリファレンスされます。 } }; int main() { FarStates my_state { .ptr{std::make_unique<TypeTeller>()}, .opt (注:指定されたテキスト「opt」はC++関連の用語と判断し、翻訳対象外としました。HTMLタグと属性はそのまま保持しています){std::in_place, TypeTeller{}}, .container{std::vector<TypeTeller>(1)}, }; my_state.from_ptr()(); my_state.from_opt()(); my_state[0](); std::cout << '\n'; std::as_const(my_state).from_ptr()(); std::as_const(my_state).from_opt()(); std::as_const(my_state)[0](); std::cout << '\n'; std::move(my_state).from_ptr()(); std::move(my_state).from_opt()(); std::move(my_state)[0](); std::cout << '\n'; std::move(std::as_const(my_state)).from_ptr()(); std::move(std::as_const(my_state)).from_opt()(); std::move(std::as_const(my_state))[0](); std::cout << '\n'; }
出力:
mutable lvalue mutable lvalue mutable lvalue const lvalue const lvalue const lvalue mutable rvalue mutable rvalue mutable rvalue const rvalue const rvalue const rvalue
関連項目
|
(C++11)
|
引数をxvalueに変換する
(関数テンプレート) |
|
(C++11)
|
関数引数を転送し、テンプレート引数の型を使用して値カテゴリを保持する
(関数テンプレート) |
|
(C++17)
|
引数への
const
参照を取得する
(関数テンプレート) |