2013-06-12 8 views
5

Ich habe eine Template-Klasse mit einem std::vector der Vorlage Argument. Das Argument darf nicht standardmäßig konstruierbar sein. Ich möchte reduzieren die Größe des Vektors (schneiden Sie es auf eine bestimmte Größe). OffensichtlichReduzieren der Größe eines std :: vector ohne einen Standardkonstruktor

vec.resize(reduced_size); 

... funktioniert nicht, da es einen Standardkonstruktor erfordert.

Ich könnte natürlich:

  1. den Standardkonstruktor für jeden verwendeten Typen erstellen (die mich erfordert es hinzufügen, wenn es nicht eine gute Design-Wahl sein könnte)
  2. übergeben Sie einen Standardwert für die Funktion (nutzlos Unordnung der Schnittstelle)
  3. ein Bauverfahren zum Template übergeben (auch nutzlos Unordnung)

Während die Frage schrieb, bemerkte ich, dass ich kann erase die Elemente aus dem Vektor bis zum Ende:

vec.erase (vec.begin() + position, vec.end()); 

... aber ich bin nicht sicher, ob dies als resize so effizient sein wird.

Gibt es eine effiziente Möglichkeit, die Größe eines Vektors ohne einen Standardkonstruktor zu reduzieren?

C++ 11 Lösungen sind akzeptabel.


EDIT: Es scheint, dass sowohl MSVC und GCC implementieren als Lösch Anruf schrumpfen die Größe, so dass meine Leistung Frage beantwortet.

+0

Ich sehe nicht, warum Löschen weniger effizient sein sollte. Es speichert 0 Bytes und ist ansonsten identisch. Haben Sie ein Benchmarking durchgeführt? – Dave

+0

http://stackoverflow.com/questions/4820998/resizing-an-stdvector-which-elements-are-beeinträchtigte –

+0

Ich bin immer noch ein C++ Anfänger, aber ich verstehe nicht, warum 'T' einen Standardkonstruktor benötigen würde wenn sie schrumpft. –

Antwort

2

In meiner Implementierung sieht es aus wie wir (mit einigen Vereinfachungen) haben:

void std::vector<T,A>::resize(size_type __new_size) 
{ 
    if (__new_size > size()) 
     _M_default_append(__new_size - size()); 
    else if (__new_size < size()) 
     _M_erase_at_end(begin() + __new_size); 
} 

auto std::vector<T,A>::erase(iterator __first, iterator __last) -> iterator 
{ 
    if (__first != __last) 
    { 
     if (__last != end()) 
      _GLIBCXX_MOVE3(__last, end(), __first); 
     _M_erase_at_end(__first + (end() - __last)); 
    } 
    return __first; 
} 

wo _M_... private Veranstaltungen Mitglied sind. Sie wollen wirklich die Effekte von _M_erase_at_end. Ich würde annehmen, dass es für einen Compiler schwierig oder unmöglich wäre, den _M_default_append Aufruf von v.resize(sz) zu optimieren, aber in v.erase(iter, v.end()) das __last == end() relativ einfach zu bemerken und die _GLIBCXX_MOVE3 und die + (end() - __last) weg zu optimieren. So könnte erase() hier sehr viel effizienter als resize() sein.

Ich würde erwarten, dass die meisten Implementierungen eine ähnliche Geschichte sein: ein paar einfache if Tests, und dann Aufruf einer identischen Methode zum Aufrufen von Destruktoren für Elemente am Ende.

+0

Leider in MSVC scheint es anders zu tun, und ich muss tragbar sein:/ –

+0

Die 'Erase' Weg ist tragbar. (Von Ihrer Frage her hat es sich so angehört, als ob Sie das bereits erkannt hätten.) (Auch ihre 'resize()' ruft Kopierkonstruktoren auf?) – aschepler

+0

Bearbeitet - es heißt nicht - überraschenderweise nennt es das Löschen im Falle von Größe ruft es einen MSVC-Bullen * auf, der die Anforderung eines Kopierkonstruktors zu instanziieren scheint. –

1

Sicher - wenn Sie resize aufrufen, können Sie einen zweiten Parameter übergeben einen Wert des richtigen Typs, der (theoretisch) verwendet werden würde, um die leeren Stellen zu füllen, wenn Sie resize verwenden, um die Größe des Vektors zu erhöhen. In C++ 03 hat dieses Argument einen Standardwert von T(), wo die Standard-Ctor in Dinge kommt (in C++ 11, sie verwenden stattdessen Überladung, so können Sie resize() anrufen, um Größe ohne weitere Schwierigkeiten zu reduzieren).

Indem Sie einen eigenen Wert übergeben, vermeiden Sie die Notwendigkeit für die Standard-Ctor. Wie oben erwähnt, wird in C++ 11 der Standard-Ctor nicht benötigt/verwendet, auch wenn Sie das zweite Argument nicht übergeben.

Ich bezweifle, dass dies eine echte Verbesserung im Vergleich zur Verwendung von erase geben wird. Insbesondere ist die Angabe in der Norm (§23.3.6.3/9):

Wenn sz <= size() entspricht erase(begin() + sz, end());.

Als solche scheint es für einen Unterschied zwischen resize und erase in diesem Fall kein wirklicher Grund zu sein.

+0

Ich sehe nicht, wie eine 'resize', auch ohne ein zweites Argument, kompilieren könnte, ohne einen Weg zu finden, ein Standard' T' zu konstruieren? Sicher, es könnte nicht länger als Standardparameter auftreten, aber wenn der Container größer wird, muss er die Daten mit etwas initialisieren. Und zur Kompilierzeit weiß der Compiler nicht, dass der Größenparameter kleiner ist als vorher. – Yakk

+0

@Yakk: Ja, wenn Sie die Größe der Auflistung erhöhen, werden Werte initialisierte Elemente eingefügt, dies erfordert jedoch Nur CopyInsertable, nicht DefaultConstructible. –

+0

In C++ 11 mit keinem zweiten Argument zu 'resize', woher kommt das' T', von dem du kopierst? – Yakk

1

Ihre Idee, erase zu verwenden, ist der richtige Weg.Um die Menge an Verwirrung zu reduzieren, würde ich einen Container basierten Algorithmus schreiben:

template<typename Container> 
Container&& reduce_size(Container&& c, std::size_t amount) { 
    using std::begin; using std::end; // enable ADL 
    amount = std::min(amount, std::size_t(end(c)-begin(c))); // paranoid! 
    c.erase(end(c)-amount, end(c)); 
    return std::forward<Container>(c); // I like my container-algorithms to pass through 
} 

, die so schnell wie Ihre erase Umsetzung sein wird (na ja, eine weitere Niederlassung und überprüfen).

Verwendung:

std::vector<Foo> blah; 
blah.emplace_back(7); 
reduce_size(blah, 10); 
+0

Upvoted. Für meine Bedürfnisse würde ich wahrscheinlich einen Cutoff mit Position als eine reduce_size mit Betrag verwenden :) –

Verwandte Themen