IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Perfect forwarding d'un vecteur de unique_ptr ?


Sujet :

C++

  1. #1
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut Perfect forwarding d'un vecteur de unique_ptr ?
    Salut à tous et à toutes !

    J'ai pour représentaire un arbre n-aire, une classe Node qui a a priori sémantique d'entité. Histoire d'essayer de gérer la propriété des noeuds correctement, j'ai utilisé un vector de unique_ptr pour représenter les enfants (choix peut être contestable).
    Pour construire un arbre à partir des feuilles, j'aimerais écrire ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    	auto f = make_node<int>(5);
    	auto g = make_node<int>(6);
     
    	std::vector<std::unique_ptr<Node<int>>> children;
    	children.push_back(std::move(f));
    	children.push_back(std::move(g));
     
    	auto e = make_parent<int>(children, 4); // là le compilateur me dit que je parle assez mal le c++
    	assert( (!f.get()) && !g.get()); // unique_ptr are now empty
    Le compilateur me dit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    error: no matching function for call to ‘make_parent(std::vector<std::unique_ptr<Node<int>, std::default_delete<Node<int> > >, std::allocator<std::unique_ptr<Node<int>, std::default_delete<Node<int> > > > >&, int)auto e = make_parent<int>(children, 4);
    Donc je me dis que j'ai foiré en essayant d'éviter la copie du vector dans la fonction friend make_parent:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    template<typename T>
    class Node;
     
    template<typename T, typename ...Args>
    std::unique_ptr<Node<T>> make_node(Args&& ...args);
     
    template<typename T, typename ...Args>
    std::unique_ptr<Node<T>> make_parent(std::vector<std::unique_ptr<Node<T>>>&& children, Args&& ...args);
     
    template<typename T>
    class Node{
         // ...
    private:
        Node() = default;
        Node(const Node&) = delete;
        Node& operator=(const Node&) = delete;
     
        template <typename... Args>
        Node(Args&&... args ) : m_data(T(std::forward<Args>(args)...)) {}
     
        template <typename ...Args>
        friend std::unique_ptr<Node<T>> make_node(Args&& ...args){
        	return std::unique_ptr<Node<T>>(new Node<T>(std::forward<Args>(args)...));
        }
     
        template<typename ...Args>
        friend std::unique_ptr<Node<T>> make_parent(std::vector<std::unique_ptr<Node<T>>>&& children, Args&& ...args){
            std::unique_ptr<Node<T>> ptr = std::make_unique<Node<T>>(new Node<T>(std::forward<std::vector<std::unique_ptr<Node<T>>>>(children), std::forward<Args>(args)...));
    	for(auto & it : children){
    	     it->m_parent = ptr.get();
    	     ptr->m_children.push_back(std::move(it));
            }
            return ptr;
        }
     
        Node<T>* m_parent = nullptr; // inexistence possible
        std::vector<std::unique_ptr<Node<T>>> m_children;
        T m_data;
    };
    J'y suis depuis hier matin, et j'ai lu plein de trucs intéressants sur stack overflow à propos de templates, des friends, des universal references, mais je manque de pratique en la matière (et je ne pourrai pas prétendre avoir tout compris à la théorie des && ). Malgré mes tentatives d'écrire la même chose de différentes façon, je ne comprends pas ce que j'ai mal fait, pourriez-vous me l'indiquer je vous prie ?
    En vous remerciant,
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Hello,

    peut-être pas la solution mais as-tu essayé auto e = make_parent<int>(std::move(children), 4); ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Hello,

    peut-être pas la solution mais as-tu essayé auto e = make_parent<int>(std::move(children), 4); ?
    bonjour Bousk ! Oui, j'ai essayé, c'est d'ailleurs le message d'erreur de trop qui m'a fait aller chercher votre aide Je ne sais en effet pas ce qu'est une "expression list treated as compound expression in functional cast error". Et google est assez peu bavard à ce sujet :/

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Node.h: In instantiation of ‘Node<T>::Node(Args&& ...) [with Args = {std::vector<std::unique_ptr<Node<int>, std::default_delete<Node<int> > >, std::allocator<std::unique_ptr<Node<int>, std::default_delete<Node<int> > > > >, int}; T = int]’:
    Node.h:116:62:   required from ‘std::unique_ptr<Node<T> > make_parent(std::vector<std::unique_ptr<Node<T> > >&&, Args&& ...) [with Args = {int}; T = int]’
    Node_test.cpp:56:50:   required from here
    Node.h:107:66: error: expression list treated as compound expression in functional cast [-fpermissive]
         Node(Args&&... args ) : m_data(T(std::forward<Args>(args)...)) {}
                                                                      ^
    /usr/include/c++/5/bits/unique_ptr.h: In instantiation of ‘typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = Node<int>; _Args = {Node<int>*}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<Node<int>, std::default_delete<Node<int> > >]’:
    Node.h:116:62:   required from ‘std::unique_ptr<Node<T> > make_parent(std::vector<std::unique_ptr<Node<T> > >&&, Args&& ...) [with Args = {int}; T = int]’
    Node_test.cpp:56:50:   required from here
    Node.h:107:5: error: ‘Node<T>::Node(Args&& ...) [with Args = {Node<int>*}; T = int]’ is private
         Node(Args&&... args ) : m_data(T(std::forward<Args>(args)...)) {}
         ^
    In file included from /usr/include/c++/5/memory:81:0,
                     from Node.h:17,
                     from Node_test.cpp:13:
    /usr/include/c++/5/bits/unique_ptr.h:765:69: error: within this context
         { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Commence par faire un truc simple, parce qu'à priori tu pars dans tous les sens et c'est difficilement lisible.
    Tu crées un node et tu ajoutes des children, make_parent c'est de la fioriture par-dessus.

    Cette ligne me parait suspecte
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::unique_ptr<Node<T>> ptr = std::make_unique<Node<T>>(new Node<T>(std::forward<std::vector<std::unique_ptr<Node<T>>>>(children), std::forward<Args>(args)...));
    Pourquoi ce n'est pas un std::move ? Et où se trouve le constructeur pour ça ? Je ne vois qu'un constructeur pour les T args..., en aucun cas les children (ce qui est logique).
    Et ton parcours qui suit est faux, si tu mouves le vector, tu parcourras un vector vide, il devra devenir for(auto & it : m_children){
    La création d'un Node, devrait être simple, parent ou pas:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    auto node = make_node...
    m_children = std::move(children);
    for (auto& child : m_children) child->parent = this;
    Si par la suite tu veux mettre ça dans un make_parent, ça n'y changera rien.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut
    Merci pour tes conseils, je fais ça (j'avais déjà un truc qui fonctionnait pour ajouter les enfants un à un, du coup je voulais factoriser un peu, mais si c'est une mauvaise idée... )
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Je n'ai pas l'impression que tu aies saisi mon message.
    Que donne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    std::unique_ptr<Node<T>> make_parent(std::vector<std::unique_ptr<Node<T>>>&& children, Args&& ...args){
            std::unique_ptr<Node<T>> ptr = std::make_unique<Node<T>>(new Node<T>(std::forward<Args>(args)...));
            ptr->m_children = std::move(children);
    	for(auto & it : ptr->m_children){
    	     it->m_parent = ptr.get();
            }
            return ptr;
        }
    ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut
    Je crois avoir bien compris ton message, c'est juste que je suis très lent
    J'avais effectivement besoin de sortir la tête du guidon pour arrêter de partir tout azimut, il manquait effectivement le constructeur avec les children.
    Je mets du coup tout le code, comme ça on sait pourquoi ça marche et vous aurez l'occasion de me dire si c'est vraiment très moche

    Le petit fichier de test qui compile et s'exécute :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    // compiles with g++ -o test Node_test.cpp -std=c++14 -Wall
     
    #include "Node.h"
    #include "assert.h"
    #include <vector>
    #include <iostream>
     
    struct Sum
    {
    	Sum(int a, int b, int c) : x(a+b+c) {}
    	int x;
    };
     
    int main(){
     
    	/***************************
            Constructors
            ***************************/
    	auto a = make_node<int>(0);
    	assert(a->get_data() == 0);
     
    	auto z = make_node<Sum>(10,20,30);
    	assert(z->get_data().x == 60);
     
    	/**************************
            Adding topology
            **************************/
    	/*           0
    	            /\ \ 
    	           1  3  4
    	          /      \ \
    	         2       5  6
    	*/
     
    	// Root -> Leaves construction
    	a->make_child(1).make_child(2);
    	Node<int>& d = a->make_child(3);
    	assert(d.has_parent());
     
    	// Leaves -> Roots construction
    	auto f = make_node<int>(5);
    	auto g = make_node<int>(6);
    	std::vector<std::unique_ptr<Node<int>>> children;
    	children.push_back(std::move(f));
    	children.push_back(std::move(g));
     
    	auto e = make_node<int>(std::move(children), 4);
     
    	assert( (!f.get()) && !g.get()); // unique_ptr are now empty
     
    	//Rebranching
    	a->add_child(std::move(e));
    	assert( !e.get());
     
    	/***************************
            Using traversals
            ****************************/
    	std::vector<int> expected_data = {0,1,2,3,4,5,6} ;
    	std::vector<int> v;
    	auto treatment = [&v](int x){ v.push_back(x); };
    	a->pre_order_DFS(treatment);
    	assert(v == expected_data);
     
    	return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    #ifndef __NODE_H_INCLUDED__
    #define __NODE_H_INCLUDED__
     
    #include <stdexcept> // logic_error
    #include <vector>
    #include <queue>
    #include <memory>
     
    template<typename T>
    class Node;
     
    template<typename T, typename ...Args>
    std::unique_ptr<Node<T>> make_node(Args&& ...args);
     
    template<typename T, typename ...Args>
    std::unique_ptr<Node<T>> make_node(std::vector<std::unique_ptr<Node<T>>>&& children, Args&& ...args);
     
    template <typename T>
    class Node{
     
    public:
    	using data_type = T;
    	~Node() = default;
     
     
    	/*************************
            / Parent tracking
            *************************/
    	bool has_parent() const{
    		return m_parent;
    	}
     
    	Node<T> const& get_parent() const{
    		if( ! has_parent() )
    			throw std::logic_error("no existing parent");			
     
    		return *m_parent;
    	}
     
    	Node<T> & get_parent() {
    		if( ! has_parent() )
    			throw std::logic_error("no existing parent");			
     
    		return *m_parent;
    	}
     
     
    	/*************************
            / Children tracking
            *************************/
    	bool has_children() const{
    		return !m_children.empty();
    	}
     
    	/*************************
            / Data access
            *************************/
    	T const& get_data() const{
    		return m_data;
    	}
     
    	T& get_data() {
    		return m_data;
    	}
     
     
    	/*************************
            / Topology edition
            *************************/
     
    	template <typename... Args>
        Node<T> & make_child(Args&&... args ) {
        	auto ptr = make_node<T>(std::forward<Args>(args)...);
        	ptr->m_parent = this;
            m_children.push_back(std::move(ptr));
            return *(m_children.back());
        }
     
        Node<T> & add_child(std::unique_ptr<Node<T>>&& child){
        	if( child->has_parent()){
        		throw std::logic_error("child already has a parent");
        	}
        	child->m_parent = this;
        	m_children.push_back(std::move(child));
    		return *(m_children.back());
        }
     
    	/*************************
            / Tree traversal
            *************************/
    	template<typename Treatment>
    	void pre_order_DFS(Treatment const& v) const {
    		v(this->get_data());
    		for(auto const& it : this->m_children){
    			it->pre_order_DFS(v);
    		}
    	}
     
    	private:
     
    	Node() = default;
        Node(const Node&) = delete;
        Node& operator=(const Node&) = delete;
     
        template <typename... Args>
        Node(Args&&... args ) : m_data(T(std::forward<Args>(args)...)) {}
     
        template <typename... Args>
        Node(std::vector<std::unique_ptr<Node<T>>>&& children, Args&&... args ) : m_children(std::move(children)), m_data(T(std::forward<Args>(args)...)) {
        	for(auto & it : m_children){
        		it->m_parent = this;
        	}
        }
     
        template <typename T1, typename ...Args>
        friend std::unique_ptr<Node<T1>> make_node(Args&& ...args);
     
        template <typename T1, typename ...Args>
        friend std::unique_ptr<Node<T1>> make_node(std::vector<std::unique_ptr<Node<T1>>>&& children, Args&& ...args);
     
    	Node<T>* m_parent = nullptr; // inexistence possible
    	std::vector<std::unique_ptr<Node<T>>> m_children;
    	T m_data;
     
    };
     
    template <typename T, typename ...Args>
    std::unique_ptr<Node<T>> make_node(Args&& ...args){
    	return std::unique_ptr<Node<T>>(new Node<T>(std::forward<Args>(args)...));
    }
     
    template <typename T, typename ...Args>
    std::unique_ptr<Node<T>> make_node(std::vector<std::unique_ptr<Node<T>>>&& children, Args&& ...args){
    	return std::unique_ptr<Node<T>>(new Node<T>(std::move(children), std::forward<Args>(args)...));
    }
     
    #endif
    En tout cas un grand merci, c'était tout bête cette histoire de constructeur, mais empếtré que j'étais je ne m'en étais pas rendu compte.
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Packet forwarder
    Par Gabuzomeu dans le forum Développement
    Réponses: 3
    Dernier message: 03/06/2003, 17h44
  2. Zoom sur des vecteurs ou lignes
    Par mat.M dans le forum Algorithmes et structures de données
    Réponses: 7
    Dernier message: 25/11/2002, 10h40
  3. Ports forwarding avec iptables
    Par Iced Earth dans le forum Réseau
    Réponses: 6
    Dernier message: 19/11/2002, 21h24
  4. matrices * vecteur
    Par delire8 dans le forum Algorithmes et structures de données
    Réponses: 15
    Dernier message: 07/09/2002, 14h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo