क्या कॉन्स्टेक्स तर्क के साथ लैम्ब्डा होना संभव है? और क्या निम्नलिखित उदाहरण को काम करना संभव है?

ForEach नीचे दिया गया फ़ंक्शन किसी दिए गए लैम्ब्डा को इंडेक्स 0, 1, 2 के साथ 3 बार कॉल करता है:

template <class Func, std::size_t... index>
inline constexpr void ForEach(Func && f, std::index_sequence<index...>)
{
    (f(index), ...);
}

template <class Func>
inline constexpr void ForEach(Func && f)
{
    ForEach(f, std::make_index_sequence<3>());
}

तो निम्नलिखित कोड

ForEach([](size_t index)
{
    std::cout << index << ' ' << std::endl;
});

आउटपुट 0, 1, 2।

लेकिन निम्न कोड जो टपल तत्वों को प्रिंट करने का प्रयास करता है, उसे एक कॉन्स्टेक्स होने के लिए index की आवश्यकता होती है:

auto t = std::make_tuple(1, 2.0, std::string("abc"));

ForEach([&t](size_t index)
{
    std::cout << std::get<index>(t) << ' ' << std::endl;
});

और इस प्रकार संकलित नहीं होता है, देखें लाइव उदाहरण। क्या किसी तरह index constexpr बनाना संभव है?

EDIT1: एक कार्यशील उदाहरण है जहां एक लैम्ब्डा तर्क का उपयोग टेम्पलेट तर्क के रूप में किया जाता है:

void Set(Tuple& val, size_t index, Variant const& elem_v)
{
    mp_with_index<std::tuple_size_v<Tuple>>(
        index,
        [&](auto I){
            std::visit([&](auto const& alt){
                if constexpr (std::is_assignable_v<
                        std::tuple_element_t<Tuple, I>,
                        decltype(alt)>)
                {
                    std::get<I>(val) = alt;
                } else {
                    throw /* something */;
                }
            }, elem_v);
        });
}

यह संकलन क्यों करता है, लेकिन मेरा नमूना कोड नहीं है?

2
Alexey Starinsky 8 जुलाई 2019, 18:10

1 उत्तर

सबसे बढ़िया उत्तर

इसमें:

ForEach([&t](size_t index)
{
    std::cout << std::get<index>(t) << ' ' << std::endl;
});

index एक स्थिर व्यंजक नहीं है। यह सिर्फ एक चर है। फ़ंक्शन पैरामीटर constexpr नहीं हैं।

लेकिन अगर हमने ForEach में कुछ बदलाव किया है (उसी तरह काम करने के लिए जिस तरह से मेरा उदाहरण आपने लिंक किया है):

template <class Func, std::size_t... index>
inline constexpr void ForEach(Func && f, std::index_sequence<index...>)
{
    (f(std::integral_constant<std::size_t, index>()), ...);
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //    instead of just index
}

ForEach([&t](auto index)
{
    std::cout << std::get<index>(t) << ' ' << std::endl;
});

तब यह काम करता है क्योंकि index अब size_t नहीं है, बल्कि विभिन्न V के लिए std::integral_constant<size_t, V> के अलग-अलग उदाहरण हैं। वह प्रकार कुछ ऐसा दिखता है:

template<class T, T v>
struct integral_constant {
    static constexpr T value = v;
    typedef T value_type;
    typedef integral_constant type; // using injected-class-name
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; } //since c++14
};

एक std::integral_constant<size_t, V> को एक size_t में कनवर्ट करना constepxr operator size_t() को आमंत्रित करता है, जिसमें इस ऑब्जेक्ट से किसी भी राज्य को पढ़ना शामिल नहीं है (जो एक खाली प्रकार है), इसलिए इसे निरंतर अभिव्यक्ति के रूप में अनुमति दी जाती है।

इसे देखने का एक अलग तरीका यह है कि हम मान को प्रकार में एन्कोड कर रहे हैं (जिसे एक स्थिर अभिव्यक्ति के रूप में पुनर्प्राप्त किया जा सकता है) न कि मान (जो नहीं कर सकता) )

8
Barry 8 जुलाई 2019, 18:25