C ++ 'da bir get_from_variant işlevi

Oct 24 2020

Bu, C ++ 'da std :: variant Implementation ile Çeşitli Tür İç İçe Yinelenebilir Bir Yinelemeli_transform Fonksiyonu için takip sorusudur . As G. Sliepen cevabı operasyonu sadece yinelemeli bırakarak dönüştürülmesi, sözü için recursive_transform()daha iyi bir fikir olabilir. Sonuç olarak, recursive_transformfonksiyonun uygulanması aşağıdaki şekilde tutulur. Ayrıca ileriye dönük beyanlar kaldırıldı.

template<class T, class _Fn> requires is_iterable<T>
static inline T recursive_transform(const T input, _Fn func)
{
    T returnObject = input;

    std::transform(input.begin(), input.end(), returnObject.begin(), func);
    return returnObject;
}

template<class T, class _Fn> requires is_iterable<T> && is_element_iterable<T>
static inline T recursive_transform(const T input, _Fn func)
{
    T returnObject = input;
    std::transform(input.begin(), input.end(), returnObject.begin(),
        [func](const auto& element)
        {
            return recursive_transform(element, func);
        }
    );
    return returnObject;
}

Bununla birlikte, yine de bileşik yapıyı aralıklarla ve std::variantgibi ele almak istiyorum std::vector<std::variant<double>>. get_from_variantBunlarla operasyonlara odaklanmak için aklıma yeni bir fonksiyon geliyor.

template<typename T_variant, typename T>
static inline auto get_from_variant(T_variant input_variant)
{
    T return_val;
    std::visit([&](auto&& arg)
        {
            return_val = static_cast<T>(arg);
            return arg;
        },
        input_variant);
    return return_val;
}

Bu get_from_variantişlevin testleri :

int main()
{
    //  get_from_variant function test
    std::variant<double> testNumber = 1;
    std::cout << get_from_variant<decltype(testNumber), double>(testNumber);
    
    //  The usage of recursive_transform function and get_from_variant function
    std::variant<double> variant_number = 3.14;
    
    std::vector<decltype(variant_number)> testVector1;
    testVector1.push_back(variant_number);
    testVector1.push_back(variant_number);
    testVector1.push_back(variant_number);
    std::cout << get_from_variant<std::variant<double>, double>(recursive_transform(testVector1, [](auto x){ return get_from_variant<std::variant<double>, double>(x) + 1; }).at(0)) << std::endl;
    
    return 0;
}

Tüm önerilere açığız.

  • Hangi soruyu takip ediyor?

    C ++ 'da std :: variant Implementation ile Çeşitli Tür İç İçe Yinelenebilen Yinelemeli_transform Fonksiyonu

  • Son sorudan bu yana kodda ne gibi değişiklikler yapıldı?

    Bileşik aralıkları ile yapı ve işlemek amacıyla std::variantgibi, std::vector<std::variant<double>>daha iyi bir şekilde, yeni bir fonksiyon get_from_variantoluşturuldu.

  • Neden yeni bir inceleme isteniyor?

    Kanımca, fonksiyonun tasarımının get_from_variantiyi olup olmadığından emin değilim ? Fikir veya kullanım iyi mi değil mi? Herhangi bir yorum açığız.

Yanıtlar

2 Quuxplusone Oct 24 2020 at 23:08

Bu konuyu başından beri takip etmedim, bu yüzden okuyucuların bu noktada olmasını beklediğinizden daha fazla kafam karıştı. Her seferinde eksiksiz bir derlenebilir örnek sağlamanız iyi bir fikir olacaktır - tıpkı bir Godbolt bağlantısı gibi, eğer sorunun odağını kodun küçük bir parçasına odaklamak istiyorsanız.

Aslında, kodunuzu Godbolt'a kendim yapıştırma zahmetinden kurtardığı için bir Godbolt bağlantısı görmeyi tercih ederim (sorudaki kodu doğru şekilde görmenize ek olarak). :) İşte kodunuza bir bağlantı: Godbolt .


std::variant<double> testNumber = 1;

Bu, C ++ 20'de derlenmez. Kullanmış mıydı? Eğer öyleyse, yikes, bu C ++ için oldukça büyük bir API molası ... ama senin sorunun değil. Her neyse, olarak değiştirin 1.0ve yeniden derleyin.


template<typename T_variant, typename T>
static inline auto

Kaybet static inline. Şablonlar tanım gereği etkin bir satır içi vardır ve yok istediğiniz bu şablon statik olmak - Eğer yok istediğiniz (o Inliner uzakta optimize edilmemiş bu durumda) kendi benzersiz kopyasını tutmak için her çeviri birimi zorlamak için.

Hayranı değilim Giraffe_case. Şablon parametre adları kısa olmalı ve CamelCase; burada tavsiye ederim V.

Sizin std::visitlambda bir işe yaramaz vardır return arg;. Aslında, tüm bu işlev daha çok

template<class V, class T>
auto get_from_variant(V input) {
    return std::visit([&](auto&& arg) {
        return static_cast<T>(arg);
    }, input);
}

Püf noktası ortadan kalktığında, bir sonraki bilgiçlik düzeyine odaklanmak için özgür beyin hücrelerimiz var: argReferansı ( auto&&) ileterek alıyorsunuz, ancak aslında onu static_cast. Belki static_cast<T>(static_cast<decltype(arg)>(arg))burada kullanmalıyız , böylece argbir rvalue referansı ise, T'ın yapıcısına taşınacak ?

Fakat bekle; asla bir rvalue referansı argolmayacak , çünkü bir lvalue ziyaret ediyoruz ! Bu yüzden belki ziyaret ettiğimiz yeri değiştirmeyi beklememeliyiz - bunu olarak alabiliriz . Ama eğer değişiklik yapmayı beklemiyorsak , belki de onu almalı - evet, bir dakika, neden buranın bir kopyasını yapıyoruz ? Başlamak için sadece const referansı ile alın!inputargconst auto& arginputinput

template<class V, class T>
auto get_from_variant(const V& input) {
    return std::visit([](const auto& arg) {
        return static_cast<T>(arg);
    }, input);
}

[&]Herhangi bir yakalama gerektirmediği için lambdadan düşürdüm .

Ayrıca şablon parametrelerine de bakmalıyız get_from_variant. Vçıkarılabilir ve çıkarılamaz T; Çıkarılamayan parametreleri ilk sıraya koymak her zaman mantıklıdır.

template<class T, class V>
auto get_from_variant(const V& input) {
    return std::visit([](const auto& arg) {
        return static_cast<T>(arg);
    }, input);
}

Şu anda ana sürücü benziyor bu :

std::variant<double> testNumber = 1.0;
std::cout << get_from_variant<double>(testNumber);
    
std::vector testVector1 = {
    std::variant<double>(3.14),
    std::variant<double>(3.14),
    std::variant<double>(3.14),
};
std::cout << get_from_variant<double>(
    recursive_transform(testVector1, [](const auto& x){
        return get_from_variant<double>(x) + 1;
    }).at(0)
) << std::endl;

Bu arada, içinde recursive_transformbir yazım hatası var: const T inputdemek istediğin zaman const T& input. Bu yazım hataları için mekanik olarak grep yapabilirsiniz ve yapmalısınız!

  • Yine static inlineşablonlardan kaldırın .

  • Ad _Fn, uygulama için ayrılmıştır; sadece kullan F.

  • Lambda'ya kopyalamak funcgerekli değildir; [&]yazdığınız her lambda için varsayılan olarak kullanmalısınız (yukarıdaki gibi düzlükten kurtulamadığınız sürece []).

  • Dürüst olmak gerekirse, STL deyimlerini takip etme konusunda küstah olmadığınız sürece , geri aramayı Fconst referansıyla iletin ve bir daha kopyalamaktan kaçının. C ++ 'da durum bilgisi olan, değiştirilebilir geri aramalar için bir yer vardır, ancak transformo yer değildir.

  • Temel durumunuz olması gerekenden daha karmaşık. Bunu düzeltelim.

Hepsini bir araya koy:

template<class T, class F>
T recursive_transform(const T& input, const F& f) {
    return f(input);
}

template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
    T returnObject = input;
    std::transform(input.begin(), input.end(), returnObject.begin(),
        [&](const auto& element) {
            return recursive_transform(element, f);
        }
    );
    return returnObject;
}

Ve sonra, gerçekten bana öyle geliyor ki, std::transformburayı kullanmak abartıdır: input iki kez okur , bir kez kopya yapmak ve tekrar dönüştürmek için. Diyelim ki onu böyle açık kodluyoruz?

template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
    T output = input;
    for (auto&& elt : output) {
        elt = recursive_transform(elt, f);
    }
    return output;
}

Tabii ki olabilir böyle bir şey yapmak 20 Aralıkları C ++ kullanmak bu :

template<class T, class F> requires is_iterable<T>
T recursive_transform(const T& input, const F& f) {
    auto transformed = input | std::views::transform([&](auto&& x) {
        return recursive_transform(x, f);
    });
    return T(transformed.begin(), transformed.end());
}

Bu, derlemek daha yavaştır ve daha büyük kod üretir - ancak çalışma zamanında gerçekten daha hızlı olabilir, eğer T::value_typekopyalamak pahalıysa, çünkü üzerinde kopyalama atamalarını ortadan kaldırıyoruz T::value_type- sadece doğrudan yerinde inşa ediyoruz.