std :: back_inserter std :: küme için?


98

Sanırım bu basit bir soru. Bunun gibi bir şey yapmam gerekiyor:

std::set<int> s1, s2;
s1 = getAnExcitingSet();
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ExcitingUnaryFunctor());

Tabii ki, std::back_inserterolmadığı için çalışmıyor push_back. std::inserterbir yineleyiciye de ihtiyaç var mı? Kullanmadım, std::inserterbu yüzden ne yapacağımı bilmiyorum.

Bir fikri olan var mı?


Elbette, diğer seçeneğim bir vektörü kullanmak s2ve daha sonra sıralamak. Belki bu daha iyi?

Yanıtlar:


143

setsahip değildir, push_backçünkü bir elemanın konumu kümenin karşılaştırıcısı tarafından belirlenir. Kullanın std::inserterve geçirin .begin():

std::set<int> s1, s2;
s1 = getAnExcitingSet();
transform(s1.begin(), s1.end(), 
          std::inserter(s2, s2.begin()), ExcitingUnaryFunctor());

Ekleme yineleyicisi daha sonra yineleyiciye yazıldığında değerin s2.insert(s2.begin(), x)nerede xolduğunu arayacaktır . Set, yineleyiciyi nereye ekleneceği konusunda bir ipucu olarak kullanır. Sen de kullanabilirsin s2.end().


2
Yana inserter(vec, vec.end())vektörler için işler de neden ilk etapta herkes kullanımının back_inserter yapar?
NHDaly

7
@NHDaly: çünkü back_inserter daha hızlı
marton78

@ marton78 Ancak, çok küçük bir farkla daha hızlı olması gerekmez mi? Bir vektör insertyerine çağırma push_back, hiçbir öğenin taşınması gerekmediğinde kabaca aynı olmalıdır (O (1)).
Felix Dombek

3
@FelixDombek haklısın, çok daha yavaş olmayacak. v.insert(x, v.end())Başlangıçta ek bir dal olacaktır (n elemanı hareket ettirdiği için, ancak burada n sıfırdır). Bununla birlikte, inserter1) kullanmak, push_back2) kullanmaktan farklı bir niyet iletir ve okuyucunun durmasına ve 3) 'ün erken bir pessimizasyon olduğunu düşünmesine neden olur.
marton78

0

2016'da "tek bir argüman inserteryineleyicisine" sahip olma önerisi vardı . https://isocpp.org/files/papers/p0471r0.html . Teklifin gelişmiş olup olmadığını bulamadım. Sanırım mantıklı.

Şimdilik, maker işlevini tanımlayan bu davranışa sahip olabilirsiniz:

template<class Container>
auto sinserter(Container& c){
    using std::end;
    return std::inserter(c, end(c));
}

Kullanım şekli:

std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});

Bunun tüm standart kaplarda çalışması amaçlanıyor mu? Std :: forward_list üzerinde çalışmaz (derleyici hatası "forward_list'in somutlaştırması içinde 'insert' adında bir üyesi yoktur" insert_iterator::operator=). Olmalı mı?
Don Hatch

@DonHatch, olan her şey insert(ve end). ilk etapta forward_listbir insertameliyat yok gibi görünüyor , sadece insert_after. Ve bu değiştirilse bile sonundan sonra eklenemez diye düşünüyorum. std::listBunun yerine a kullanamaz mısın?
alfC

Elbette, kişisel olarak std :: forward_list'e ihtiyacım yok. Ancak genel bir "bir kapsayıcıyı diğerine nasıl kopyalarım?" İle ilgileniyorum. mantıklı olduğu tüm kap çiftleri için. Şu anki ilgi alanım @HowardHinnant'ın short_alloc gibi genel kapsayıcı ayırıcıları kullanmak.
Don Hatch

@DonHatch, mesele şu ki, bu sorunun birçok çeşidi var. ("" bir konteyneri diğerine nasıl kopyalarım? "). Örneğin, orijinal değerleri korumak mı istiyorsunuz, tahsisleri en aza indirmek mi istiyorsunuz? En basit durumda bence en iyi cevap, iki yineleyici alan konteyneri inşa edin (çoğu konteynır bunu alabilir) NewContaner new_container(old_other_container.begin(), old_other_container.end()).
alfC

1
Evet, std :: set bir SequentialContainer değil ve sorun değil :-) existing_list = std::list(c.begin(), c.end(), existing_list.get_allocator()) Çok güzel, sanırım cevabım bu. Şerefe!
Don Hatch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.