web-dev-qa-db-ja.com

C ++で非const変数を反復する方法は?

#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にバインドできないため、このコードはコンパイルされません。

同様の構成を機能させる簡単な方法はありますか、つまり、ここでabのように、異なる変数にある値を反復処理します。

41
tmlen

{a,b}abのコピーを作成しています。可能な解決策の1つは、ループ変数をポインターにして、abのアドレスを取得することです。

#include <initializer_list>

struct Obj {
    int i;
};

Obj a, b;

int main() {
    for(auto obj : {&a, &b}) {
        obj->i = 123;   
    }
}

ご覧ください live

注:一般的には、autoを使用する方が適切です サイレント暗黙変換を回避する

51
francesco

これが機能しない理由は、 _std::initializer_list_ の基本要素がaおよびbからコピーされ、タイプ_const Obj_なので、基本的には定数値を可変参照にバインドしようとしています。

これを使用してこれを修正しようとすることができます:

_for (auto obj : {a, b}) {
    obj.i = 123;
}
_

しかしすぐに、オブジェクトia内の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が彼の答えで行ったように、ループで生のポインタを使用することでこれを行う方が簡単に見えるかもしれませんが、私は生のポインタをできるだけ回避する癖があり、この場合、参照を使用すると、コードがより明確でよりクリーンになりました。

44
ruohola

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に移動コンストラクタしかない場合でも機能します。

5
Jacob Manaker