Um zusammenzufassen, was bisher vorgeschlagen wurde:
- Verwenden
vector<unique_ptr<T>>
- Fügt eine explizite Dereferenzierungsebene und war unerwünscht durch OP.
- Verwenden Sie
deque<T>
- Ich versuchte zuerst deque
, aber das Löschen von Objekten von ihm funktioniert auch nicht. See this discussion auf Unterschiede zwischen deque
und list
.
Die Lösung ist forward_list
zu verwenden, die eine einfach verknüpfte Liste (oder Sie können list
verwenden, wenn Sie eine doppelt verknüpfte Liste wollen). Wie @JanHudec darauf hingewiesen hat, erfordern vector
(und viele seiner Freunde) eine Neuzuweisung beim Hinzufügen oder Entfernen von Elementen. Das passt nicht gut zu Objekten wie mutex
und atomic
, die nicht kopiert oder verschoben werden dürfen. forward_list
und list
erfordern dies nicht, da jede Zelle unabhängig zugewiesen wird (ich kann den Standard nicht angeben, aber die Indexierungsmethode führt zu dieser Annahme). Da sie tatsächlich verknüpfte Listen sind, unterstützen sie keine Indexierung mit wahlfreiem Zugriff. myList.begin() + i
erhalten Sie einen Iterator der i
'th Element, aber es (am sichersten) wird durch alle vorherigen i
Zellen zuerst durchlaufen müssen.
Ich habe die Versprechungen vom Standard nicht angeschaut, aber die Dinge funktionieren gut unter Windows (Visual Studio) und CompileOnline (g ++). Fühlen Sie sich frei mit dem folgenden Testfall zu spielen, um auf CompileOnline:
#include <forward_list>
#include <iostream>
#include <mutex>
#include <algorithm>
using namespace std;
class X
{
/// Private copy constructor.
X(const X&);
/// Private assignment operator.
X& operator=(const X&);
public:
/// Some integer value
int val;
/// An object that can be neither copied nor moved
mutex m;
X(int val) : val(val) { }
};
int main()
{
// create list
forward_list<X> xList;
// add some items to list
for (int i = 0; i < 4; ++i)
xList.emplace_front(i);
// print items
for (auto& x : xList)
cout << x.val << endl;
// remove element with val == 1
// WARNING: Must not use remove here (see explanation below)
xList.remove_if([](const X& x) { return x.val == 1; });
cout << endl << "Removed '1'..." << endl << endl;
for (auto& x : xList)
cout << x.val << endl;
return 0;
}
Ausgang:
Executing the program....
$demo
3
2
1
0
Removed '1'...
3
2
0
ich dies erwarten, etwa die gleiche Leistung wie vector<unique_ptr<T>>
haben (solange Sie nicht zufällig verwenden Zugriffsindizierung zu oft).
WARNUNG: Die Verwendung von forward_list::remove
funktioniert derzeit nicht in VS 2012. Das liegt daran, dass das Element kopiert wird, bevor versucht wird, es zu entfernen. Die Header-Datei Microsoft Visual Studio 11.0\VC\include\forward_list
(gleiches Problem in list
) zeigt:
void remove(const _Ty& _Val_arg)
{ // erase each element matching _Val
const _Ty _Val = _Val_arg; // in case it's removed along the way
// ...
}
So wird kopiert "falls es auf dem Weg entfernt ist".Dies bedeutet, dass list
und forward_list
nicht einmal die Speicherung von unique_ptr
zulassen. Ich nehme an, dass dies ein Designfehler ist. Die Umgehung ist einfach: Sie müssen remove_if
anstelle von remove
verwenden, da die Implementierung dieser Funktion nichts kopiert.
Ein Großteil des Kredits geht auf die anderen Antworten. Da jedoch keine von ihnen eine vollständige Lösung ohne Zeiger war, beschloss ich, diese Antwort zu schreiben.
Wenn Sie tolerieren können, dass alle Ihre Thread-Pool-Objekte standardmäßig konstruiert sind und stattdessen andere Methoden anbieten, um sie außer ctor/dtor live/tot zu machen, würde 'std :: array' den Trick machen? –
@JoeZ Derzeit kann mein Pool in der Größe variieren (Threads können hinzugefügt/entfernt werden). Wenn es keine bessere Lösung gibt, kann ich das berücksichtigen und dynamische Zuordnungsfunktionen entfernen. – Domi
Nun, die Anzahl der aktiven Threads kann sich ändern, aber die Lebensdauern der Threads sind nicht perfekt verschachtelt. Da Sie die Objekte nicht verschieben können, enthält die Threadpoolstruktur "Lücken". Ein Array fühlt sich "richtig" an, wenn Sie bei jeder Aktion keine Umleitung wünschen. Sie werden wahrscheinlich immer noch mit Zeigern enden, die Ihre Active-Thread-Liste und Ihre Inactive-Thread-Liste zusammenführen. –