c++ - काम नहीं कर रहे लूप के लिए निर्दोष रेंज




for-loop c++17 (4)

समाधान: एक संदर्भ आवरण का उपयोग करें

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;
}

यह हालांकि पहले सवाल का जवाब देने की कोशिश नहीं करता है।

निम्नलिखित संकलन नहीं करता है:

#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;
}

इसे गॉडबोल्ट पर आज़माएं

संकलक त्रुटि है: error: assignment of read-only reference 's'

अब मेरे वास्तविक मामले में सूची एक वर्ग पर सदस्य चर से बनी है।

अब, यह काम नहीं करता है क्योंकि अभिव्यक्ति एक initializer_list<int> बन जाती है जो वास्तव में a, b, c और d को कॉपी करती है - इसलिए संशोधन की अनुमति भी नहीं देती है।

मेरा सवाल दो गुना है:

क्या इस तरह लूप के लिए रेंज-आधारित लिखने की अनुमति नहीं देने के पीछे कोई प्रेरणा है? जैसे। शायद नग्न ब्रेस अभिव्यक्ति के लिए एक विशेष मामला हो सकता है।

इस प्रकार के लूप को ठीक करने का एक वाक्यात्मक स्वच्छ तरीका क्या है?

इस लाइन के साथ कुछ पसंद किया जाएगा:

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

मैं सूचक को अप्रत्यक्ष रूप से एक अच्छा समाधान नहीं मानता (वह है {&a, &b, &c, &d} ) - किसी भी समाधान को तत्व का संदर्भ सीधे तब देना चाहिए जब पुनरावृत्त को डी-संदर्भित किया जाए


आप संदर्भ को संग्रहीत करने के लिए रैपर क्लास बना सकते हैं और इस मान को अद्यतन करने के लिए असाइनमेंट ऑपरेटर होगा:

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

लाइव डेमो


एक आवरण विचार के भीतर बस एक और समाधान:

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

मानक के अनुसार .611.6.4 सूची-आरंभीकरण / p5 [dcl.init.list] [ जोर मेरा खदान ]:

एक प्रकार की वस्तु 'std :: initializer_list' का निर्माण एक initializer सूची से किया जाता है जैसे कि कार्यान्वयन उत्पन्न और उत्प्रेरित (7.4) प्रकार का एक प्रकार "N const E का सरणी" , जहाँ N, initializer सूची में तत्वों की संख्या है। उस ऐरे के प्रत्येक तत्व को इनिशियलाइज़र सूची के संबंधित तत्व के साथ कॉपी-इनिशियलाइज़ किया जाता है, और उस एरे को संदर्भित करने के लिए std :: initializer_list ऑब्जेक्ट का निर्माण किया जाता है। [नोट: कॉपी के लिए चयनित एक कंस्ट्रक्टर या रूपांतरण फ़ंक्शन आरंभिक सूची के संदर्भ में सुलभ (क्लाज 14) होगा। - अंतिम नोट] यदि किसी भी तत्व को शुरू करने के लिए एक संकीर्ण रूपांतरण की आवश्यकता होती है, तो कार्यक्रम बीमार है।

इस प्रकार, आपका कंपाइलर वैध रूप से शिकायत कर रहा है (यानी, int const& s auto &s डिडक्ट्स और आप लूप के लिए राउंडेड s में असाइन नहीं कर सकते हैं)।

आप एक आरंभिक सूची (उदाहरण के लिए, 'std :: वेक्टर') के बजाय 'std :: reference_wrapper' के बजाय एक कंटेनर शुरू करके इस समस्या को कम कर सकते हैं:

#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;
}

लाइव डेमो





initializer-list