C ++, vektöre kopyala


149

Kopyalamam std::setgereken yer std::vector:

std::set <double> input;
input.insert(5);
input.insert(6);

std::vector <double> output;
std::copy(input.begin(), input.end(), output.begin()); //Error: Vector iterator not dereferencable

Sorun nerede?


5
assign()işlevi de var :output.assign(input.begin(), input.end());
Gene Bushuyev

vektörünüz boş. İnsanların aşağıda işaret ettiği gibi, bunu çözmenin birçok yolu vardır.
AJG85

@Gene: assign (), önceden gerekli depolama miktarını rezerve etmek () istiyor. Yineleyiciler kesinlikle InputIterator olmadıkça ne kadar gerekli olduğunu belirlemek için girdi yineleyicileri kullanır, bu durumda ayırmayı atlar ve her push_back () 'de yeniden tahsisle sonuçlanır. Spektrumun diğer ucunda, BiderectionalIterators, sadece end - begin'ı çıkarmasına izin verecektir. std :: set'in yineleyicileri ikisi de değildir (ForwardIterator'durlar) ve bu talihsiz bir durumdur: bu durumda, assign () sadece boyutunu belirlemek için tüm kümeyi dolaşır - büyük kümelerde kötü performans.
Sergey Shevchenko

Yanıtlar:


216

Kullanmanız gereken back_inserter:

std::copy(input.begin(), input.end(), std::back_inserter(output));

std::copyeklediğiniz kaba öğe eklemez: eklemez; yalnızca kapsayıcıya bir yineleyiciye sahiptir. Bu nedenle, bir çıktı yineleyicisini doğrudan ' std::copya iletirseniz, bunun en azından giriş aralığını tutacak kadar büyük bir aralığa işaret ettiğinden emin olmalısınız.

std::back_inserterpush_backher öğe için bir konteyneri çağıran bir çıktı yineleyicisi oluşturur , böylece her öğe konteynere eklenir. Alternatif olarak, std::vectorkopyalanan aralığı tutmak için içinde yeterli sayıda öğe oluşturmuş olabilirsiniz :

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

Veya std::vectoraralık yapıcısını kullanabilirsiniz :

std::vector<double> output(input.begin(), input.end()); 

3
Merhaba James, std :: copy satırınız yerine (cevabınızdaki ilk kod bloğu), output.insert(output.end(), input.begin(), input.end());bunun yerine yapamaz mıyım ?
user2015453

veya sadece cbegin ve cend versiyonunu kullanın: output.insert(output.cend(), input.cbegin(), input.cend());Ne düşünüyorsunuz? Teşekkürler.
user2015453

2
Output.reserve (input.size ()); kendi başıma veya bazı derleyicilerin benim için yapmasını umabilir miyim?
jimifiki

@jimifiki, korkarım umut yok.
Alexis Wilke

İlk vektör başlatmanız yanlış. input,size()Boş girişlerden oluşan bir dizi oluşturursunuz ve ardından sonları eklersiniz. Sanırım kullanmak istiyorsun std::vector<double> output; output.reserve(input.size()); std::copy(...);.
Alexis Wilke

124

Yineleyicileri alan vektör için yapıcıyı kullanın:

std::set<T> s;

//...

std::vector v( s.begin(), s.end() );

Yalnızca v'nin içindeki s'nin içeriğini istediğinizi ve veriyi ona kopyalamadan önce v'de hiçbir şey olmadığını varsayar.


42

işte başka bir alternatif vector::assign:

theVector.assign(theSet.begin(), theSet.end());

24

Vektör nesnenizde kümenizin içeriğini tutacak kadar alan ayırmadınız.

std::vector<double> output(input.size());
std::copy(input.begin(), input.end(), output.begin());

1
Bu -1'i hak etmiyor. Özellikle, bu vektörün yalnızca bir tahsis yapmasına izin verir (çünkü O (1) 'deki küme yineleyicilerin mesafesini belirleyemez) ve inşa edildiğinde her bir elemanı sıfırlamak için vektör için tanımlanmamışsa, bu kopyanın bir memcpy olarak kaynatılmasına izin vermeye değer. İkincisi, uygulama vektörün ctor'undaki döngüyü çıkarırsa, yine de faydalı olabilir. Tabii ki, birincisi rezerv ile de elde edilebilir.
Fred Nurk

Bilmiyorum. Sana yardım etmeme izin ver.
wilhelmtell

Sana -1 verdim, ama benim açımdan bir thinko idi. Oyumu geri alabilmem için küçük bir düzenleme yapın ve size +1 vereceğim: Bu aslında fail-first özelliği nedeniyle çok temiz bir çözüm.
Fred Foo

Sadece cevabı kendim düzenlersem, olumlu oy verebileceğimi anladım. Bunu yaptım, başarısız olan ilk bellek tahsisi için size +1 verdi. Afedersiniz!
Fred Foo

3

Bence en verimli yol, öğeleri önceden tahsis etmek ve sonra yerleştirmek:

template <typename T>
std::vector<T> VectorFromSet(const std::set<T>& from)
{
    std::vector<T> to;
    to.reserve(from.size());

    for (auto const& value : from)
        to.emplace_back(value);

    return to;
}

Bu şekilde, önce varsayılan kurucuyu çağırmak ve ardından yukarıda listelenen diğer çözümler için atama işlecini kopyalamak yerine, her öğe için yalnızca kopya oluşturucuyu çağıracağız. Aşağıda daha fazla açıklama.

  1. back_inserter kullanılabilir ancak vektör ( https://en.cppreference.com/w/cpp/iterator/back_insert_iterator ) üzerinde push_back () işlevini çağıracaktır . emplace_back () daha etkilidir çünkü push_back () kullanılırken geçici oluşturmaktan kaçınır . Önemsiz olarak oluşturulmuş türlerle ilgili bir sorun değildir, ancak önemsiz biçimde oluşturulmuş türler (örneğin std :: string) için bir performans uygulaması olacaktır.

  2. Tüm öğelerin varsayılan olarak yapılandırılmasına (hiçbir şey için) neden olan boyut argümanına sahip bir vektör oluşturmaktan kaçınmamız gerekir. Örneğin std :: copy () kullanan çözümde olduğu gibi.

  3. Ve sonunda, vector :: assign () yöntemi veya yineleyici aralığını alan yapıcı iyi seçenekler değildir çünkü set yineleyicilerinde std :: distance () (eleman sayısını bilmek için) çağırırlar. Bu , set İkili Arama Ağacı veri yapısı olduğundan ve rasgele erişim yineleyicileri uygulamadığından , tüm set elemanlarında istenmeyen ek yinelemelere neden olur .

Umarım yardımcı olur.


lütfen bunun neden hızlı olduğu ve back_inserter
a'nın

Cevaba daha fazla açıklama eklendi.
dshvets1

1

std::copyboş bir kaba yerleştirmek için kullanılamaz. Bunu yapmak için, aşağıdaki gibi bir insert_iterator kullanmanız gerekir:

std::set<double> input;
input.insert(5);
input.insert(6);

std::vector<double> output;
std::copy(input.begin(), input.end(), inserter(output, output.begin())); 

3
Bu, vektör yeniden ayırdığında başarısız olur: output.begin () öğesinden gelen yineleyici geçersiz kılınır.
Fred Nurk
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.