Je voudrais initialiser un std::map
statique où la valeur n'est pas copiable. Je vais appeler ma classe ValueClass. ValueClass a un std::unique_ptr
en tant que membre privé et je m'assure même que ValueClass n'est pas copiable en développant non_copyable
qui ressemble à ce qui suit:
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;
};
Maintenant j'essaye de définir std :: map en utilisant ma classe comme valeur:
static std::map<int, ValueClass> value_classes = {
{0, ValueClass()},
{1, ValueClass() }
};
Je reçois une erreur de compilation lorsque initializer_list
essaie de copier cette classe.
J'ai essayé d'écrire ma propre fonction make_map
en entier ce week-end pendant plusieurs heures pour permettre l'initialisation sans copie mais j'ai échoué. J'ai essayé this , that et other mais aucun d'entre eux ne compile avec Visual Studio 15.9.4.
Comment puis-je initialiser std :: map statique où la copie n'est pas forcée et l'initialisation est uniformisée dans une fonction, à l'aide du compilateur Visual Studio?
EDIT: Voici la version simplifiée du scénario de la vie réelle dans laquelle j'essaie de faire en sorte que cela fonctionne (pardonnez-moi, faute de convention de désignation et d'incohérence pour les cas):
#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: La solution fournie dans cette question ne fonctionne pas avec la structure de classe que j'ai. Je cherche également une solution pour corriger la fonction make_map_by_moving
, autrement dit une initialisation en ligne, la réponse étant qu'il existe une solution impérative avec des appels de fonction.
Vous ne pouvez pas le faire directement, car initializer_list
a const
sauvegarde pour tous ses éléments - et ils doivent être copiés de la liste d'initialisation dans le conteneur. Cela nécessite évidemment une copie. Malheureusement, il n’ya aucun moyen de créer une liste d’initialisation.
En C++ 17, grâce à l’élision de copie garantie, vous pouvez le faire:
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();
Ce code n'effectue aucune copie sur non_copyable
. Nous utilisons la construction dans map
, puis beacuse get()
est une valeur, il n’existe pas de copie/déplacement de get()
dans value_classes
. La m
dans get()
est l'objet value_classes
.
Une approche légèrement sneaker consisterait à abuser de try_emplace()
pour ceci:
std::map<int, non_copyable> get() {
std::map<int, non_copyable> m;
m.try_emplace(0);
m.try_emplace(1);
return m;
}
try_emplace()
prend le type de clé par lui-même (vous pouvez donc simplement passer un int
), puis les arguments de la valeur pour la mise en place séparément, ce qui permet de le faire beaucoup moins verbalement.
Je pense que vous devez créer l'objet avec insert_or_assign
dans une fonction puis le renvoyer:
std::map<int, ValueClass> populate()
{
std::map<int, ValueClass> value_classes;
value_classes.insert_or_assign(std::make_pair(0, ValueClass());
return value_classes;
}
Et votre initialisation devient:
std::map<int, ValueClass> value_classes = populate();
Mais alors, cette classe a un destructeur virtuel, ce qui signifie que vous voulez réellement être un std::map<int, std::unique_ptr<ValueClass>>
et non une carte d'objets réels (vous ne savez pas à quoi vont servir ces objets?).
Modifier après la question modifier:
Dans ce cas, 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;
}
Incluez également functional
.
Vous ne pouvez simplement pas utiliser initializer_list
pour move
un objet à partir d'un objet non-copyable
.
Votre classe supprime les copy constructor
& assignment operator
. Lorsque vous essayez d'initialiser votre map
ou toute autre container
avec un initializer_list
, le initializer_list
vous oblige strictement à référencer une LValue
et interdit la sémantique de déplacement ou de transfert de RValue
.
Voici un article de blog très agréable qui explique tous les détails: knatten.org ainsi qu'un Q/A similaire trouvé ici .