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


11

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

#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>जो वास्तव में क, ख, ग, और घ की नकल करता है - इसलिए संशोधन की अनुमति भी नहीं देता है।

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

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

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

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

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

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


1
एक साधारण वर्कअराउंड (कि मैं वास्तव में खुद का उपयोग नहीं करूंगा) इसके बजाय संकेत की एक सूची बनाना है { &a, &b, &c, &d }:।
कुछ प्रोग्रामर ने

2
initializer_listconstसरणी पर ज्यादातर एक दृश्य है ।
Jarod42

मैं शायद एक-एक करके चर को स्पष्ट रूप से शुरू करने के लिए क्या करूंगा। यह लिखने के लिए बहुत अधिक नहीं होने जा रहा है, यह स्पष्ट और स्पष्ट है, और यह वही करता है जो इरादा है। :)
कुछ प्रोग्रामर ने

3
अगर आप नहीं चाहते हैं { &a, &b, &c, &d }, तो आप न तो चाहते हैं:for (auto& s : std::initializer_list<std::reference_wrapper<int>>{a, b, c, d}) { s.get() = 1; }
Jarod42

सवाल "क्यों यह काम करने में सक्षम नहीं है" यह "मैं इस काम की तरह कुछ बनाने के लिए क्या कर सकता हूं" से एक बहुत अलग सवाल है?
निकोल बोलस

जवाबों:


4

रंग उतने जादू नहीं हैं जितने लोग चाहेंगे। अंत में, एक ऑब्जेक्ट होना चाहिए जो कंपाइलर किसी सदस्य फ़ंक्शन या मुफ्त फ़ंक्शन पर कॉल उत्पन्न कर सकता है 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;
}

1
तुम गिरा सकते हो std::vector<int*>
1242 में Jarod42

@ मुल्होलोमोन ने स्पष्ट रूप से कहा है कि एक सूचक अप्रत्यक्ष समाधान में कोई दिलचस्पी नहीं है।
darune

1
यह होना चाहिए auto sया auto* sनहीं auto& s
LF

@ डार्यून - मुझे खुशी होगी कि किसी ने अलग जवाब दिया। यह स्पष्ट नहीं है कि वर्तमान मानक के साथ ऐसा उत्तर मौजूद है।
mhhollomon

@ एलएफ - सहमत।
mhhollomon

4

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

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

2
यह एक सभ्य और अच्छा समाधान / समाधान है। इसका एक विचार मेरे अपने उत्तर के अनुकूल है (मैंने एक std :: reference_wrapper और वैरिएडिक टेम्पलेट का उपयोग नहीं किया)
darune

4

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

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

इस प्रकार, आपका कंपाइलर वैध रूप से शिकायत कर रहा है (यानी, auto &sकटौती करता है int const& 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;
}

लाइव डेमो


@ Jarod42 Ouups क्षमा करें, इसमें संशोधन किया गया।
101010

आप समाधान एक अच्छे समाधान के लिए मेरे मानदंड को फिट नहीं करते हैं - अगर मैं इससे खुश था कि मैंने पहली जगह में नहीं पूछा होगा :)
darune

यह भी कि आपका उद्धरण मेरे प्रश्न का उत्तर देने का प्रयास नहीं करता है
darune

1
@ डारुने - वास्तव में, उद्धरण वह कारण है जो आपके for (auto& s : {a, b, c, d})काम नहीं करता है। जैसे कि मानक में वह खंड क्यों है ..... आपको मानकीकरण समिति के सदस्यों से पूछना होगा। इस तरह की कई बातों की तरह, तर्क "हम आपके विशेष मामले के बारे में परेशान करने के लिए पर्याप्त उपयोगी नहीं था" के बीच कुछ भी हो सकता है "आपके मामले का समर्थन करने के लिए मानक के कई अन्य हिस्सों को बदलना होगा, और हमने विचार स्थगित कर दिया।" जब तक हम भविष्य का मानक विकसित नहीं कर लेते हैं ”।
पीटर

तुम सिर्फ इस्तेमाल नहीं कर सकते std::array<std::reference_wrapper>>?
टोबी स्पाइट

1

उस वाक्य रचना को संतुष्ट करने के लिए

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

डेमो


1
इससे अलग कैसे होता है std::reference_wrapper?
टोबी स्पाइट

1
@TobySpeight: std::reference_wrapperकी आवश्यकता होगी s.get() = 1;
Jarod42

0

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

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

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


-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

लाइव डेमो

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.