Ich möchte einen statischen std::map
initialisieren, bei dem der Wert nicht kopierbar ist. Ich werde meine Klasse ValueClass nennen. ValueClass hat einen std::unique_ptr
als privates Mitglied, und ich stelle sogar sicher, dass ValueClass nicht kopierbar ist, indem Sie non_copyable
erweitern, der wie folgt aussieht:
class non_copyable {
public:
non_copyable() = default;
protected:
virtual ~non_copyable() = default;
private:
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
Jetzt versuche ich eine std :: map mit meiner Klasse als Wert zu definieren:
static std::map<int, ValueClass> value_classes = {
{0, ValueClass()},
{1, ValueClass() }
};
Ich erhalte einen Kompilierungsfehler, da initializer_list
versucht, diese Klasse zu kopieren.
Ich habe versucht, an diesem Wochenende über viele Stunden hinweg meine eigene make_map
-Funktion zu schreiben, um die Initialisierung ohne Kopieren zu ermöglichen, aber ich bin fehlgeschlagen. Ich habe this , that und other ausprobiert, aber keiner von ihnen kann mit Visual Studio 15.9.4 kompilieren.
Wie kann ich statische std :: map initialisieren, wenn die Kopie nicht erzwungen wird und die Initialisierung in einer Funktion unter Verwendung des Visual Studio-Compilers vereinheitlicht wird?
EDIT: Hier ist die vereinfachte Version des realen Szenarios, in dem ich versuche, dies zum Laufen zu bringen.
#include <iostream>
#include <map>
class non_copyable {
public:
non_copyable() = default;
protected:
virtual ~non_copyable() = default;
private:
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
class InnerValueClass : public non_copyable
{
public:
InnerValueClass(const int inner_number) : inner_number_(inner_number) { }
private:
int inner_number_;
};
class ValueClass : public non_copyable
{
public:
ValueClass(const int number1) : number1_(number1) { }
ValueClass(const bool condition) : condition_(condition), inner_value_(
std::make_unique<InnerValueClass>(5)) { }
private:
int number1_{};
bool condition_{};
std::unique_ptr<InnerValueClass> inner_value_{};
};
/* Inline initialization of std::map copies, this is for initialization of non-copy types*/
template <typename TKey, typename TNonCopyableValue>
class make_map_by_moving
{
typedef std::map<TKey, TNonCopyableValue> map_type;
map_type map_;
public:
make_map_by_moving(const TKey& key, TNonCopyableValue&& val)
{
map_.emplace(key, std::move(val));
}
make_map_by_moving<TKey, TNonCopyableValue>& operator()(const TKey& key, TNonCopyableValue&& val)
{
map_.emplace(key, std::move(val));
return *this;
}
operator const map_type&()
{
return map_;
}
};
static std::map<int, ValueClass> map =
make_map_by_moving<int, ValueClass>
(1, ValueClass(5))
(2, ValueClass(true));
/* It goes on like this for hundreds of lines, so I really appreciate any
solution that leave me with a clean initialization rather than calling
functions on std::map */
int main() { }
Duplicate edit: Die in dieser Frage bereitgestellte Lösung funktioniert nicht mit der Klassenstruktur, die ich habe. Ich suche auch nach einer Lösung, um die make_map_by_moving
-Funktion zu korrigieren, mit anderen Worten eine Inline-Initialisierung. Die Antwort gibt eine zwingende Lösung mit Funktionsaufrufen.
Sie können dies nicht direkt tun, da initializer_list
für alle Elemente const
unterstützt. Diese Elemente müssen aus der Initialisierungsliste in den Container kopiert werden. Das erfordert natürlich das Kopieren. Es gibt leider keine Möglichkeit, von einer Initialisierungsliste zu wechseln.
In C++ 17 können Sie dank der garantierten Kopienauswahl Folgendes tun:
std::map<int, non_copyable> get() {
std::map<int, non_copyable> m;
m.emplace(std::piecewise_construct, std::Tuple(0), std::Tuple());
m.emplace(std::piecewise_construct, std::Tuple(1), std::Tuple());
return m;
}
std::map<int, non_copyable> value_classes = get();
Dieser Code führt keine Kopien von non_copyable
aus. Wir emstruct das Konstrukt innerhalb von map
, und dann ist get()
ein prvalue. Es gibt keine Kopie/Verschiebung von get()
in value_classes
. Die m
in get()
ist das Objekt value_classes
.
Ein etwas sneaker-Ansatz wäre, try_emplace()
dafür zu missbrauchen:
std::map<int, non_copyable> get() {
std::map<int, non_copyable> m;
m.try_emplace(0);
m.try_emplace(1);
return m;
}
try_emplace()
nimmt den Schlüsseltyp von selbst (so können Sie einfach eine int
übergeben) und dann die Argumente für den Wert für das separate Einfügen, was einen viel weniger wortreichen Weg dazu bringt.
Ich denke, Sie müssen das Objekt mit insert_or_assign
in einer Funktion erstellen und es dann zurückgeben:
std::map<int, ValueClass> populate()
{
std::map<int, ValueClass> value_classes;
value_classes.insert_or_assign(std::make_pair(0, ValueClass());
return value_classes;
}
Und Ihre Initialisierung wird zu:
std::map<int, ValueClass> value_classes = populate();
Diese Klasse hat jedoch einen virtuellen Destruktor, was bedeutet, dass Sie tatsächlich einen std::map<int, std::unique_ptr<ValueClass>>
und nicht eine Karte der tatsächlichen Objekte (nicht wissen, wofür diese Objekte verwendet werden sollen) sein soll.
Bearbeiten Sie nach der Bearbeitung der Frage:
In diesem Fall Barrys suggestion is the one to follow, using
emplace`:
std::map<int, ValueClass> populate()
{
std::map<int, ValueClass> value_classes;
value_classes.emplace(1, 5);
return value_classes;
}
Fügen Sie auch functional
hinzu.
Sie können einfach initializer_list
nicht für move
ein Objekt aus einem non-copyable
-Objekt verwenden.
Ihre Klasse löscht den copy constructor
& assignment operator
. Wenn Sie versuchen, Ihre map
oder eine andere container
mit einem initializer_list
zu initialisieren, zwingt der initializer_list
Sie streng, auf eine LValue
zu verweisen, und verbietet RValue
die Semantik verschieben oder vorwärts.
Hier ist ein sehr schöner Blogartikel, der alle Details erläutert: knatten.org sowie eine ähnliche Frage/Antwort hier .