লুপ কাজ না করার জন্য ভিত্তিক নির্দোষ পরিসর


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_listবেশিরভাগ constঅ্যারেতে দেখা হয়।
জারোড 42

আমি সম্ভবত যা করব তা হ'ল স্পষ্টভাবে একের পর এক চলকগুলি সূচনা করা। এটি লেখার মতো আরও বেশি কিছু ঘটবে না, এটি পরিষ্কার এবং সুস্পষ্ট and :)
কিছু প্রোগ্রামার

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*>
জারোড 42

@ এমএলহোলোমন আমি স্পষ্টভাবে বলেছি যে আমি পয়েন্টার ইন্ডিরিশন সলিউশনে আগ্রহী নই।
দারুনে

1
এটি হবে auto sবা auto* sনা auto& s
এলএফ

@ সদরুন - আমি কাউকে অন্যরকম উত্তর দিতে পেরে খুশি হব। এটি পরিষ্কার নয় যে এই জাতীয় উত্তরটি বর্তমান মানদণ্ডের সাথে বিদ্যমান।
mhhollੋਮ

@ এলএফ - সম্মত
mhhollੋਮ

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
এটি একটি শালীন এবং ভাল সমাধান / কর্মক্ষেত্র। এটি আমার নিজের উত্তরটির সাথে
সমান

4

স্ট্যান্ডার্ড -11.6.4 অনুসারে তালিকা-সূচনা / পি 5 [dcl.init.list] [ জোর দেওয়া খনি ]:

প্রারম্ভিক তালিকা থেকে 'স্টাড :: ইনিশিয়ালাইজার_লিস্ট' টাইপের একটি অবজেক্ট তৈরি করা হয় যেন বাস্তবায়ন উত্পন্ন হয় এবং ম্যাটেরিয়ালাইজড হয় (.4.৪) "এন কনট ই অ্যারে" টাইপের একটি মূল্য , যেখানে এন প্রাথমিককরণ তালিকার উপাদানগুলির সংখ্যা। সেই অ্যারের প্রতিটি উপাদান প্রাথমিক সূচকের সাথে সম্পর্কিত উপাদানটির সাথে অনুলিপি করা হয়, এবং সেই অ্যারে উল্লেখ করার জন্য std :: initializer_list অবজেক্টটি নির্মিত হয়। [দ্রষ্টব্য: অনুলিপিটির জন্য নির্বাচিত কোনও কনস্ট্রাক্টর বা রূপান্তর ফাংশন প্রারম্ভিক তালিকার প্রেক্ষাপটে অ্যাক্সেসযোগ্য (ধারা 14) হবে। - শেষ দ্রষ্টব্য] যে কোনও উপাদানকে আরম্ভ করার জন্য যদি সংকীর্ণ রূপান্তর প্রয়োজন হয় তবে প্রোগ্রামটি দুর্গঠিত।

সুতরাং, আপনার সংকলক বৈধভাবে অভিযোগ করছে (অর্থাত্, এর জন্য auto &sকর্তন হয় int const& sএবং আপনি sলুপের জন্য সীমাবদ্ধ করতে পারেন না )।

আপনি 'স্টাড :: রেফারেন্স_ওয়ার্পার' দিয়ে প্রাথমিক স্তরের তালিকার পরিবর্তে একটি ধারক প্রবর্তন করে (যেমন, `স্টাড :: ভেক্টর) প্রবর্তন করে এই সমস্যাটি দূর করতে পারেন:

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

সরাসরি নমুনা


@ জারোড 42 ওউপস দুঃখিত, এটি সংশোধন করেছেন।
101010

আপনার সমাধানটি একটি ভাল সমাধানের জন্য আমার মাপদণ্ডের সাথে খাপ
খায়

এছাড়াও আপনার উদ্ধৃতি আমার প্রশ্নের উত্তর দেওয়ার চেষ্টা করে না
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
@ টবিস্পাইট: std::reference_wrapperপ্রয়োজন হবে s.get() = 1;
জারোড 42

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

সরাসরি নমুনা

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.