Std :: qatorlari ketma-ketligini birlashtirish

Quyidagilarni ko'rib chiqing: ( Wandbox )

#include 
#include 
#include 

template
auto concat(const std::array& ar1, const std::array& ar2)
{
    std::array result;
    std::copy (ar1.cbegin(), ar1.cend(), result.begin());
    std::copy (ar2.cbegin(), ar2.cend(), result.begin() + N);
    return result;
}

int main()
{
    std::array ar1 = {1, 2, 3};
    std::array ar2 = {4, 5};
    auto result = concat(ar1, ar2);
    for (auto& x : result)
        std::cout << x << " ";
    std::cout << std::endl;
    return 0;
}

Given a sequence of std::array, std::array, ..., std::array, how can I generalize the above code and write a function which concatenates the sequence into an std::array?

Yuqorida keltirilgan misolda, masalan, concat dan foydalaning, masalan, maxsus usulni yozmasdan, bu ikkilik operatsiyani qo'llash orqali o'xshash ketma-ketlikdagi shablon sinflarini qisqartiradigan qayta foydalanish mumkin funktsiyani yozishning bir usuli yaxshi bo'lsa yaxshi bo'lardi (ikkilik op o'zgarishlarini har safar qayta yozish kerak bo'ladi).

(IIUC, standart kutubxonaning tegishli algoritmlari ( accumulate , reduce ) faqat ikkilik operatsion natijasi sinfi har doim bir xil bo'lsa ishlaydi.)

9
@chris Bu toza, bu vazifani bilmas edi. Ammo yuqorida yozganimdek, har qanday shablonli ikki tomonlama operatsiyani turli sinflardagi ob'ektlar ketma-ketligiga qo'llagan holda echimga ega bo'lish yaxshi bo'lardi (albatta, operatsiya assotsiativ bo'lishi va barcha o'rta natijalarga tatbiq etish uchun to'g'ri bo'lishi kerak)
qo'shib qo'ydi muallif Danra, manba
Bizda std :: tuple_cat bor. Bitta variant shunchaki natijadan olingan nusxadan bir qatorga aylantirishni yozadi.
qo'shib qo'ydi muallif chris, manba

8 javoblar

Quyidagilarni qilishingiz mumkin:

template 
auto func(F f, T&& t, T2&& t2)
{
    return f(std::forward(t), std::forward(t2));
}

template 
auto func(F f, T&& t, T2&& t2, Ts&&...args)
{
    return func(f, f(std::forward(t), std::forward(t2)), std::forward(args)...);
}

Foydalanish bilan

struct concatenater
{
    template
    auto operator()(const std::array& ar1, const std::array& ar2) const
    {
        std::array result;
        std::copy (ar1.cbegin(), ar1.cend(), result.begin());
        std::copy (ar2.cbegin(), ar2.cend(), result.begin() + N);
        return result;
    }
};

va

auto result = func(concatenater{}, ar1, ar2, ar3, ar4);

Namoyish

7
qo'shib qo'ydi
Chiroyli.
qo'shib qo'ydi muallif Danra, manba

Given a sequence of std::array, std::array, ..., std::array, how can I write a function which concatenates the sequence into an std::array?

Mana, C ++ 17 yechimi. Bu, ehtimol, qisqartirilishi va takomillashishi mumkin.

template 
constexpr auto with_acc_sizes(TF&& f, const TArray& array, const TRest&... rest)
{
    f(array, std::integral_constant{});

    if constexpr(sizeof...(TRest) != 0)
    {
        with_acc_sizes>(f, rest...); 
    }
}

template
constexpr auto concat(const std::array&... arrays)
{
    std::array result{};

    with_acc_sizes([&](const auto& arr, auto offset)
    {
        std::copy(arr.begin(), arr.end(), result.begin() + offset);
    }, arrays...);

    return result;
}

Foydalanish:

std::array ar1 = {1, 2, 3};
std::array ar2 = {4, 5};
std::array ar3 = {6, 7, 8};
auto result = concat(ar1, ar2, ar3);

jonli wandbox misoli

Ham g ++ 7 va clang ++ 5 bilan ishlaydi.

6
qo'shib qo'ydi
@chris: bu juda yoqimli yechim - men uni javob sifatida yuborishingiz kerak deb o'ylayman.
qo'shib qo'ydi muallif Vittorio Romeo, manba
Bu savolga C ++ 11 savolini yozib olgani juda yaxshi. Shu sababli, C ++ 17 yaxshilanishlarini bir joyda saqlashni xohlayman.
qo'shib qo'ydi muallif chris, manba
Mana mening. Har qanday qismni ishlatishdan bexisiz: melpon.org/wandbox/permlink/arkvF7HpZGr4LKOa . Buni std :: copy dan foydalanmaganimning yagona sababi constexpr ni ushlab turish edi, chunki bu o'xshashlik ko'rinardi.
qo'shib qo'ydi muallif chris, manba
@Oliv: So'raladigan kod size_t o'rniga integral_constant yuborilgandan keyin, agar u so'ralganda qiymati doimiy iboralarda (masalan, shablon argumenti) foydalanishi mumkin.
qo'shib qo'ydi muallif ildjarn, manba
@Oliv: funktsiyalar argumentlari hech qachon constexpr ; shuning uchun istalgan qiymatni turiga ( integral_constant ) kiritishda muhim ahamiyatga ega. Lambdaning constexpr bo'lsin yoki bo'lmasin, doimiy kontekstda unga funktsiya argumenti sifatida qiymatini kiritish kerakmi yoki bo'lmasin, 100% diktol hisoblanadi.
qo'shib qo'ydi muallif ildjarn, manba
@Oliv: with_acc_sizes - bu umumiy interfeys (nom va nom maydoni bo'yicha); faqat concat da doimiy kod sifatida offset shart emas, chunki u barcha potentsial qidiruvchilarni to'g'ri joylashtirish uchun yozilmasligi kerak. Agar with_acc_sizes nom maydoni tafsilotida bo'lsa yoki faqat qidiruvchini concat deb bilsak, sen bilan. : -]
qo'shib qo'ydi muallif ildjarn, manba
Mening C ++ 17 hissam: to'liq ko'chirma, nusxa ko'chirish o'rniga rvalu yozuvlaridan ko'chirish: melpon.org/wandbox/ permlink/5O1X7JTAfJ7d40sT constexpr talablari ( constexpr ) hech qanday shablon parametrlari hech qachon qoniqtirilmagani uchun concat texnik jihatdan UBga sabab bo'ladi std :: copy hech qachon constexpr ).
qo'shib qo'ydi muallif ildjarn, manba
@VittorioRomeo Nima uchun f deb chaqirasiz? f (array, std :: integral_constant {}) qator, oxirgi) ? f ning konstexpr baholashini faollashtirishni xohlaysizmi?
qo'shib qo'ydi muallif Oliv, manba
@ildjarn Ha, lekin C ++ 17 constexpr lambda ni amalga oshiradi, shuning uchun inliningdan keyin hosil qilingan kod bir xil bo'ladi deb o'ylardim. Boshqa tomondan, ular lambda operatori bilan aloqa qilmasa() oxirgi shaklda foydalanib, shuncha operatorni ishlab chiqaradi (chunki ularning kodi shaffof bo'lishiga olib kelishi mumkin) Ko'p marta noyob funktsiya chindan ham do'sti. Menda shubha bor, shuning uchun men bu yaxshi naqsh yoki nayrangbozlikmi deb o'ylayman.
qo'shib qo'ydi muallif Oliv, manba
@ildjarn Rahmat qilaman, men nima haqida gaplashayotganingizni aniq tushunaman. Lekin offset argumenti, konstexpr bo'lishi kerak bo'lgan biron bir ifodada ishtirok etmaydi. Agar siz std :: integral_constant Last bo'yicha oxirgi> {} kodini o'zgartirsangiz, natijada olingan optimallashtirilgan kod bir xil bo'ladi. Optimallashtirishni inline qilmasdan, agar siz faqat Last dan foydalansangiz, natijada kod kichikroq bo'ladi. Lambda-ni inline yoki noto'g'ri tanlashga qaror qilgan optimizerga bog'liq holda, derivatga yordam berishga harakat qilmasangiz, natijada kod bir xil yoki yaxshi bo'ladi!
qo'shib qo'ydi muallif Oliv, manba
@ildjarn, rahmat, endi sizning nuqtai nazaringizni tushunaman. Men bu kabi "tag-dispetcher" ni texnikadan boshqa narsa uchun ishlatish kabi hech qachon o'ylamagan edim!
qo'shib qo'ydi muallif Oliv, manba

Bu erda katlama ifodalari orqali oddiy C ++ 17 echimini topishingiz mumkin:

#include 
#include 

template 
auto concatenate(const std::array&... arrays)
{
    std::array result;
    std::size_t index{};

    ((std::copy_n(arrays.begin(), sizes, result.begin() + index), index += sizes), ...);

    return result;
}

Foydalanish misoli:

const std::array array1 = {{1, 2, 3}};
const std::array array2 = {{4, 5}};
const std::array array3 = {{6, 7, 8, 9}};

const auto result = concatenate(array1, array2, array3);

Jonli demo

2
qo'shib qo'ydi
Yaxshi! Umumiy versiya uchun bir qavatni ifodasini ishlatish uchun har qanday fikrlar (o'zboshimchalik bilan ikkilik op uchun)?
qo'shib qo'ydi muallif Danra, manba
@Danra Generic versiyasi bu holatda juda samarali bo'lmaydi.
qo'shib qo'ydi muallif Constructor, manba

C ++ 14.

template
using index_t=std::integral_constant;
template
constexpr index_t index{};

template
auto index_over(std::index_sequence){
  return [](auto&&f)->decltype(auto){
    return decltype(f)(f)( index... );
  };
}
template
auto index_upto(index_t={}){
  return index_over(std::make_index_sequence{});
}

Bu bizga inline parametr paketlarini kengaytirish imkonini beradi.

template
using indexed_type=T;

template
std::decay_t concat_arrays( T&& in ){ return std::forward(in); }
template
std::array
concat_arrays( std::array arr0, std::array arr1 ){
  auto idx0 = index_upto();
  auto idx1 = index_upto();
  return idx0( [&](auto...I0s){
    return idx1( [&](auto...I1s)->std::array{
      return {{
        arr0[I0s]...,
        arr1[I1s]...
      }}; 
    })
  });
}

bizni ikkiga oladi. N uchun, oson yo'li:

template
auto concat_arrays( std::array arr0, std::array arr1, std::array... arrs ){
  return concat_arrays( std::move(arr0), concat_arrays( std::move(arr1), std::move(arrs)... ) );
}

Lekin, bu rekursiyasiz amalga oshirilishi mumkin.

Kod sinovdan o'tkazilmagan.

2
qo'shib qo'ydi

Birinchi fikrlash qatori qatorni mos yozuvlar to'plamiga aylantirishni (kod) o'zgartirish, tuple_cat bilan ishlov berishni ko'rib chiqamiz va so'ngra oxirgi qatorni yaratish uchun har qanday amalni bajarish kerak (ya'ni harakat yoki nusxa olish - dastlab o'tgan argumentlarga qarab):

#include 
#include 

namespace detail {
    template
    auto array_as_tie(Array &a, std::index_sequence) {
        return std::tie(a[Is]...);
    };

    template
    auto copy_to_array(Tuple &t, std::index_sequence) {
        return std::array
                {
                        std::get(t)...
                };
    };

    template
    auto move_to_array(Tuple &t, std::index_sequence) {
        return std::array
                {
                        std::move(std::get(t))...
                };
    };
}

template
auto array_as_tie(std::array &a) {
    return detail::array_as_tie(a, std::make_index_sequence());
};


// various overloads for different combinations of lvalues and rvalues - needs some work
// for instance, does not handle mixed lvalues and rvalues yet
template
auto array_cat(std::array &a1, std::array &a2) {
    auto tied = std::tuple_cat(array_as_tie(a1), array_as_tie(a2));
    return detail::copy_to_array(tied, std::make_index_sequence());
};

template
auto array_cat(std::array &&a1, std::array &&a2) {
    auto tied = std::tuple_cat(array_as_tie(a1), array_as_tie(a2));
    return detail::move_to_array(tied, std::make_index_sequence());
};

int main() {
    std::array ar1 = {1, 2, 3};
    std::array ar2 = {4, 5};

    auto result = array_cat(ar1, ar2);

    for (auto &x : result)
        std::cout << x << " ";
    std::cout << std::endl;

   //move-construction
    auto r2 = array_cat(std::array {"a", "b"},
                        std::array{"asdfghjk", "qwertyui"});

    std::cout << "string result:\n";
    for (auto &&x : r2)
        std::cout << x << " ";
    std::cout << std::endl;

    return 0;
}

kutilgan natijalar:

1 2 3 4 5 
string result:
a b asdfghjk qwertyui 
2
qo'shib qo'ydi

To'liq C ++ 11; @ Jarod42 kabi o'qilishi mumkin emas, balki ko'plab qatorlar bilan potentsial jihatdan ancha samarali bo'lishi mumkin, agar chaqiriq daraxti butunlay tekislanmagan bo'lsa (inlining holatida), bir nechta vaqtinchalik, progressiv rivojlangan natija ob'ektlaridan ko'ra bitta natija obyekti mavjud:

namespace detail {
    template
    struct sum_sizes_;

    template
    struct sum_sizes_ : std::integral_constant { };

    template
    struct sum_sizes_ : sum_sizes_ { };

    template
    using sum_sizes_t = typename sum_sizes_<
        0, std::tuple_size::type>{}...
    >::type;

    template
    void transfer(R& ret, typename std::remove_reference::type const& a) {
        std::copy(a.begin(), a.end(), ret.begin() + O);
    }

    template void transfer(R& ret, typename std::remove_reference::type&& a) {
        std::move(a.begin(), a.end(), ret.begin() + O);
    }

    template
    void concat(R const&) { }

    template
    void concat(R& ret, A&& a, As&&... as) {
        transfer(ret, std::forward(a));
        concat<(O + sum_sizes_t{})>(ret, std::forward(as)...);
    }
}

template<(sizeof...(As) >= 2), int>::type = 0>
auto concat(As&&... as)
-> std::array<
    typename std::common_type::type::value_type...>::type,
    detail::sum_sizes_t{}
> {
    decltype(concat(std::forward(as)...)) ret;
    detail::concat<0>(ret, std::forward(as)...);
    return ret;
}

Onlayn ko'rsatuv

Shuni esda tutingki, bu ham std :: move std :: copy o'rniga rvalues ​​uchun algoritm .

1
qo'shib qo'ydi
Sum_sizes_t = ... qatoridan da typename va :: turi o'chirishingiz mumkin
qo'shib qo'ydi muallif Danra, manba
@Danra: Men ularni maqsadga muvofiq deb bilaman. : -] Bularni olib tashlash bu erda bu erda ishlaydi, lekin men faqatgina SFINAE emas, balki ixtisoslashuvda ishlatilishi mumkin, shuning uchun aynan menda metafunksiyalarni tanlangan turdagi o'rniga aniq std :: integral_constant ni qaytarishni afzal ko'raman.
qo'shib qo'ydi muallif ildjarn, manba

Bu umumlashtirilmaydi, biroq, agar biz ikkita qatorni bir-biriga yaqinlashtirsak, yangi qatorni ishga tushirish uchun foydalanishimiz mumkin.

Qanday qilib foydali umumlashma har qanday holatda bo'lgani uchun men ishonmayman. Bir-biriga mos bo'lmagan o'lchamdagi qatorlarni hisobga olsak, ular bilan nima qilish kerak, ammo barchasini birlashtirasizmi?

#include 
#include 
#include 

template
constexpr std::array concat_aux(const std::array& l, std::index_sequence,
                                          const std::array& r, std::index_sequence) {
    return std::array { std::get(l)..., std::get(r)... };
}

template
constexpr std::array concat(const std::array& l, const std::array& r) {
    return concat_aux(l, std::make_index_sequence{},
                      r, std::make_index_sequence{});
}

template
constexpr auto concat(const std::array& l,
                      const std::array& r,
                      const std::array&... arrays) {
    return concat(concat(l, r), arrays...);
}

int main() {
    std::array a1{1, 2, 3, 4, 5};
    std::array a2{6, 7, 8};
    std::array a3{9, 10};

    for (const auto& elem : concat(a1, a2, a3)) {
        std::cout << elem << " ";
    }
}
1
qo'shib qo'ydi

constexpr dan foydalangan holda C ++ 17 echimini ko'chirish mumkin bo'lgan turlari bilan to'g'ri ishlaydi.

template
inline constexpr auto array_size = std::tuple_size_v>;

template
constexpr auto make_array(Ts&&... values)
{
    using T = std::common_type_t;
    return std::array{static_cast(std::forward(values))...};
}

namespace detail
{
template
constexpr auto array_cat(Arr1&& arr1, Arr2&& arr2, std::index_sequence, std::index_sequence)
{
    return make_array(std::get(std::forward(arr1))...,
        std::get(std::forward(arr2))...);
}
}

template
constexpr auto array_cat(Arr&& arr, Arrs&&... arrs)
{
    if constexpr (sizeof...(Arrs) == 0)
        return std::forward(arr);
    else if constexpr (sizeof...(Arrs) == 1)
        return detail::array_cat(std::forward(arr), std::forward(arrs)...,
            std::make_index_sequence>{},
            std::make_index_sequence>{});
    else
        return array_cat(std::forward(arr), array_cat(std::forward(arrs)...));
}
0
qo'shib qo'ydi