[c++] 루프가 작동하지 않는 무고한 범위

다음은 컴파일 되지 않습니다 .

#include <iostream>

int main()
{
    int a{},b{},c{},d{};

    for (auto& s : {a, b, c, d}) {
        s = 1;
    }
    std::cout << a << std::endl;
    return 0;
}

Godbolt에서 사용해보십시오

컴파일러 오류는 다음과 같습니다 error: assignment of read-only reference 's'

이제 실제 경우에는 목록이 클래스의 멤버 변수로 구성됩니다.

이제는 표현식이 initializer_list<int>실제로 a, b, c 및 d를 복사하는 표현식이되기 때문에 작동하지 않습니다 . 따라서 수정도 허용하지 않습니다.

내 질문은 두 가지입니다.

이런 식으로 범위 기반 for 루프를 작성하지 못하게하는 동기가 있습니까? 예. 벌거 벗은 괄호 표현에는 특별한 경우가있을 수 있습니다.

이 유형의 루프를 고정 하는 구문적인 깔끔한 방법은 무엇입니까 ?

이 라인을 따르는 것이 선호됩니다.

for (auto& s : something(a, b, c, d)) {
    s = 1;
}

포인터 간접 참조를 좋은 솔루션이라고 생각하지 않습니다. 즉 {&a, &b, &c, &d}, 반복자가 참조 해제 될 때 모든 솔루션이 요소 참조를 직접 제공해야합니다 .



답변

범위는 사람들이 원하는만큼 마술이 아닙니다. 결국, 컴파일러가 멤버 함수 또는 자유 함수 begin()및에 대한 호출을 생성 할 수있는 오브젝트가 있어야합니다 end().

당신이 올 수있는 가장 가까운 것은 :

#include <iostream>

int main()
{
    int a{},b{},c{},d{};

    for (auto s : {&a, &b, &c, &d} ) {
        *s = 1;
    }
    std::cout << a << "\n";
    return 0;
}


답변

래퍼 아이디어 내의 또 다른 솔루션 :

template<typename T, std::size_t size>
class Ref_array {
    using Array = std::array<T*, size>;

    class Iterator {
    public:
        explicit Iterator(typename Array::iterator it) : it_(it) {}

        void operator++() { ++it_; }
        bool operator!=(const Iterator& other) const { return it_ != other.it_; }
        decltype(auto) operator*() const { return **it_; }

    private:
        typename Array::iterator it_;
    };

public:
    explicit Ref_array(Array args) : args_(args) {}

    auto begin() { return Iterator(args_.begin()); }
    auto end() { return Iterator(args_.end()); }

private:
    Array args_;
};

template<typename T, typename... Ts>
auto something(T& first, Ts&... rest) {
    static_assert((std::is_same_v<T, Ts> && ...));
    return Ref_array<T, 1 + sizeof...(Ts)>({&first, &rest...});
}

그때:

int main() {
    int a{}, b{}, c{}, d{};

    for (auto& s : something(a, b, c, d)) {
        std::cout << s;
        s = 1;
    }

    std::cout  << std::endl;
    for (auto& s : something(a, b, c, d))
        std::cout << s;
}

출력

0000
1111


답변

표준 §11.6.4 List-initialization / p5 [dcl.init.list] [ Emphasis Mine ] 에 따르면 :

‘std :: initializer_list’유형의 객체는 구현이 “array of N const E”유형의 prvalue를 생성하고 구체화 한 것처럼 (7.4) 이니셜 라이저 목록에서 구성됩니다. 여기서 N은 초기화 목록의 요소 수입니다. 해당 배열의 각 요소는 초기화 목록의 해당 요소로 복사 초기화되고 std :: initializer_list 객체는 해당 배열을 참조하도록 구성됩니다. [참고 : 사본을 위해 선택된 생성자 또는 변환 함수는 이니셜 라이저 목록의 컨텍스트에서 액세스 할 수 있어야합니다 (Clause 14). — end note] 요소를 초기화하기 위해 축소 변환이 필요한 경우 프로그램이 잘못 구성됩니다.

따라서 컴파일러는 합법적으로 불평하고 있습니다 (즉, 범위가 지정된 루프에서 auto &s공제되어 int const& s할당 할 수 없습니다 s).

‘std :: reference_wrapper’를 사용하여 초기화 목록 대신 컨테이너 (예 :`std :: vector ‘)를 도입하여이 문제를 완화 할 수 있습니다.

#include <iostream>
#include <vector>
#include <functional>

int main()
{
    int a{},b{},c{},d{};

    for (auto& s : std::vector<std::reference_wrapper<int>>{a, b, c, d}) {
        s.get()= 1;
    }
    std::cout << a << std::endl;
    return 0;
}

라이브 데모


답변

그 구문을 만족시키기 위해

for (auto& s : something{a, b, c, d}) {
    s = 1;
}

랩퍼를 작성할 수 있습니다.

template <typename T>
struct MyRefWrapper
{
public:
    MyRefWrapper(T& p)  : p(&p) {}

    T& operator =(const T& value) const { return *p = value; }

    operator T& () const { return *p; }
private:
    T* p;
};

데모


답변

솔루션 : 참조 랩퍼 사용

template <class It>
struct range_view_iterator : public It{//TODO: don't inherit It
    auto& operator*() {
        return (*this)->get();
    }
};

template<class It>
range_view_iterator(It) -> range_view_iterator<It>;


template<class T>
struct range_view {
    std::vector<std::reference_wrapper<T> > refs_;
    range_view(std::initializer_list<std::reference_wrapper<T> > refs) : refs_{refs} {
    }

    auto begin() {
        return range_view_iterator{ refs_.begin() };
    }

    auto end() {
        return range_view_iterator{ refs_.end() };
    }
};

그런 다음 다음과 같이 사용하십시오.

for (auto& e : range_view<int>{a, b, c, d}) {
    e = 1;
}

그래도 첫 번째 질문에 대답하지는 않습니다.


답변

참조를 저장하기위한 랩퍼 클래스를 작성할 수 있으며이 값을 업데이트하기위한 지정 연산자가 있습니다.

template<class T>
struct Wrapper {
    T& ref;

    Wrapper(T& ref)
    : ref(ref){}

    template<class U>
    void operator=(U u) {
        ref = u;
    }
};

template<class...T>
auto sth(T&...t) {
    return std::array< Wrapper<std::common_type_t<T...> > ,sizeof...(t) >{Wrapper(t)...};
};

int main(){
    int a{},b{},c{},d{};

    for (auto s : sth(a,b,c,d)) {
        s = 1;
    }
    std::cout << a << std::endl; // 1

라이브 데모


답변