#include <initializer_list>
struct Obj {
int i;
};
Obj a, b;
int main() {
for(Obj& obj : {a, b}) {
obj.i = 123;
}
}
initializer_list
{a, b}
からの値はconst Obj&
として取得され、非const参照obj
にバインドできないため、このコードはコンパイルされません。
同様の構成を機能させる簡単な方法はありますか、つまり、ここでa
やb
のように、異なる変数にある値を反復処理します。
{a,b}
a
とb
のコピーを作成しています。可能な解決策の1つは、ループ変数をポインターにして、a
とb
のアドレスを取得することです。
#include <initializer_list>
struct Obj {
int i;
};
Obj a, b;
int main() {
for(auto obj : {&a, &b}) {
obj->i = 123;
}
}
ご覧ください live
注:一般的には、auto
を使用する方が適切です サイレント暗黙変換を回避する
これが機能しない理由は、 _std::initializer_list
_ の基本要素がa
およびb
からコピーされ、タイプ_const Obj
_なので、基本的には定数値を可変参照にバインドしようとしています。
これを使用してこれを修正しようとすることができます:
_for (auto obj : {a, b}) {
obj.i = 123;
}
_
しかしすぐに、オブジェクトi
とa
内のb
の実際の値が変化しなかったことに気付くでしょう。その理由は、ここで auto
を使用すると、ループ変数obj
の型がObj
になるため、ループするだけです。 a
およびb
のコピー。
これを修正する実際の方法は、 _std::ref
_ ( _<functional>
_ ヘッダーで定義)を使用して、イニシャライザ内のアイテムを作成することです。リストはタイプ _std::reference_wrapper<Obj>
_ です。これは暗黙的に_Obj&
_に変換できるため、ループ変数の型として保持できます。
_#include <functional>
#include <initializer_list>
#include <iostream>
struct Obj {
int i;
};
Obj a, b;
int main()
{
for (Obj& obj : {std::ref(a), std::ref(b)}) {
obj.i = 123;
}
std::cout << a.i << '\n';
std::cout << b.i << '\n';
}
_
出力:
_123 123
_
上記を行う別の方法は、ループで_const auto&
_および _std::reference_wrapper<T>::get
_ を使用することです。 _reference_wrapper
_は変更されず、ラップする値だけが変更されるため、ここでは定数参照を使用できます。
_for (const auto& obj : {std::ref(a), std::ref(b)}) {
obj.get().i = 123;
}
_
しかし、ここでauto
を使用すると.get()
の使用が強制されるため、これは非常に扱いにくく、前者の方法がこれを解決するための好ましい方法だと思います。
@francescoが彼の答えで行ったように、ループで生のポインタを使用することでこれを行う方が簡単に見えるかもしれませんが、私は生のポインタをできるだけ回避する癖があり、この場合、参照を使用すると、コードがより明確でよりクリーンになりました。
a
およびb
のコピーが望ましい動作である場合は、初期化子リストではなく一時配列を使用できます。
#include <initializer_list>
struct Obj {
int i;
} a, b;
int main() {
typedef Obj obj_arr[];
for(auto &obj : obj_arr{a, b}) {
obj.i = 123;
}
}
これは、Obj
に移動コンストラクタしかない場合でも機能します。