web-dev-qa-db-fra.com

Initialiser std :: map statique avec une valeur non copiable dans une initialisation en ligne uniforme

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.

8
U. Bulle

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.

6
Barry

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, usingemplace`:

std::map<int, ValueClass> populate()
{
    std::map<int, ValueClass> value_classes;
    value_classes.emplace(1, 5);
    return value_classes;
}

Incluez également functional.

4
Matthieu Brucher

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 .

2
Francis Cugler