# base::OwnedRef [base::OwnedRef](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/bind.h;l=357;drc=7824b6f569e030d369cb58ba731b9c56eaeb6d93)とはChromiumの中でmutableなオブジェクトをcallbackに束縛したいときに使われるヘルパー関数。 適当に使ってたら詰まったので確認してみる。 ## 概要 ```cpp void foo(int& arg) { std::cout << ++arg << std::endl; } int counter = 0; RepeatingClosure foo_callback = BindRepeating(&foo, OwnedRef(counter)); foo_callback.Run(); // 1 foo_callback.Run(); // 2 foo_callback.Run(); // 3 std::cout << counter << std::endl; // 0 ``` 実行例からわかるように、base::OwnedRefはコピーを作っていることに注意。 元の変数の方の値はcallbackによって変わっていない。 実装を見てみる。 ```cpp template <typename T> internal::OwnedRefWrapper<std::decay_t<T>> OwnedRef(T&& t) { return internal::OwnedRefWrapper<std::decay_t<T>>(std::forward<T>(t)); } template <typename T> class OwnedRefWrapper { public: explicit OwnedRefWrapper(const T& t) : t_(t) {} explicit OwnedRefWrapper(T&& t) : t_(std::move(t)) {} T& get() const { return t_; } private: mutable T t_; }; ``` const参照を受け取ってmutableとして保存する。 ちなみにdecay_tは推論された型を取るやつ。例えば`char[]`を渡すと`char*`に変身する。 Bindで渡されるArg型はいくつかの条件を満たしていないといけない。 条件は[ParamCanBeBound](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/bind_internal.h;l=1444;drc=f4a00cc248dd2dc8ec8759fb51620d47b5114090)の中の[`value`](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/bind_internal.h;l=1577-1585;drc=f4a00cc248dd2dc8ec8759fb51620d47b5114090)として計算される。 例えば以下はForwardできるかを確認している。 ```cpp! template <bool v = UnwrappedParam::kCanBeForwardedToBoundFunctor || !UnwrappedParam::kIsUnwrappedForwardableNonConstReference> struct NonConstRefParamMustBeWrapped { ... } // kIsUnwrappedForwardableNonConstReferenceの実体 static constexpr bool kIsUnwrappedForwardableNonConstReference = std::is_lvalue_reference_v<FunctorParamType> && !std::is_const_v<std::remove_reference_t<FunctorParamType>> && std::is_convertible_v<std::decay_t<ForwardingType>&, FunctorParamType>; ``` const referenceならOKで、そうでないならwrapしていないといけないので、unwrapかつnon-const refのとき`v`がfalseになるようになっている。 ## いろんな渡し方 base::OwnedRefの他にもいくつかの渡し方があるので確認する。 ### base::Owned Ownedの例としては以下。 ```cpp! // Refじゃないのでポインタとして受け取る void foo(int* arg) { std::cout << *arg << std::endl; } int* pn = new int(1); RepeatingClosure foo_callback = BindRepeating(&foo, Owned(pn)); // Prints "1" foo_callback.Run(); foo_callback.Run(); *pn = 2; // Prints "2" foo_callback.Run(); // `pn` is deleted. foo_callback.Reset(); ``` 所有権をcallbackに渡す。 渡す側が参照を持つ形式。 ### base::Passed Passedはunique_ptrなどcopyはできないがmoveは許されている型のオブジェクトを渡す時に使う。 ちなみに以下のような条件がParamsCanBeBoundにある。 ```cpp! static constexpr bool kWouldBeForwardableWithPassed = std::is_convertible_v<std::decay_t<ForwardingType>&&, FunctorParamType>; ``` 比較用に、普通にForwardedできる条件は以下。 ```cpp! static constexpr bool kCanBeForwardedToBoundFunctor = std::is_convertible_v<ForwardingType, FunctorParamType>; ``` passでいけるやつは`&&`、つまりmove operatorならconvertibleのとき。 TODO(elkurin): decay_tは何してる? ## ちなみにstd::ref [std::ref](https://en.cppreference.com/w/cpp/utility/functional/ref)も参照ラッパーとして変数への参照を保持する標準ライブラリ。 以下は最近書いた使用例。 ```cpp base::ThreadPool::PostTaskAndReply( FROM_HERE, base::MayBlock(), base::BindOnce(&DoLacrosBackgroundWorkPreLaunch, lacros_dir, clear_shared_resource_file, launching_at_login_screen, std::ref(params)), base::BindOnce(std::move(callback))); ``` `params`の所有権はどこかに保存したままその参照を`std::ref`で取り出して渡すことでcallbackにも無事渡せている。 返り値はreference_wrapper型。