Puis-je liste d'initialisation d'un vecteur de mouvement-type seul?
Si je passe le code suivant à travers mon GCC 4.7 de l'instantané, il essaie de copier les unique_ptr
s dans le vecteur.
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Évidemment cela ne peut pas fonctionner, car std::unique_ptr
n'est pas copiable:
erreur: utilisation de supprimé la fonction 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [avec _Tp = int; _Dp = std::default_delete; std::unique_ptr<_Tp, _Dp> = std::unique_ptr]'
Est GCC corriger en essayant de copier les pointeurs de la liste d'initialiseur?
Visual Studio et clang a le même comportement
OriginalL'auteur R. Martinho Fernandes | 2011-12-12
Vous devez vous connecter pour publier un commentaire.
Le synopsis de
<initializer_list>
dans 18.9 fait assez clairement que les éléments d'une liste d'initialiseur sont toujours transmis via const référence. Malheureusement, il ne semble pas être un moyen de déplacer à l'aide de la sémantique dans l'initialiseur de la liste des éléments dans la révision actuelle de la langue.Plus précisément, nous avons:
Ne semble pas si "idiomatiques pour moi": n'est-il pas, au lieu de cela, pur UB? non pas seulement comme l'itérateur, mais plutôt les éléments qui sous-tendent eux-mêmes pourraient être
const
, qui ne peuvent être jetées dans un puits formé programme.OriginalL'auteur Kerrek SB
Edit: Depuis @Johannes ne semble pas vouloir poster la meilleure solution, une réponse, je vais juste le faire.
Les itérateurs retourné par
std::make_move_iterator
va déplacer le-pointu à l'élément d'être déréférencé.Réponse originale à cette question:
Nous allons utiliser un peu de type d'assistance ici:
Malheureusement, le simple code suivant ne fonctionne pas:
Depuis la norme, pour quelque raison que ce soit, ne définit pas une conversion de constructeur de copie comme ceci:
La
initializer_list<rref_wrapper<move_only>>
créé par le corset-init-liste ({...}
) de ne pas se convertir à l'initializer_list<move_only>
que levector<move_only>
prend. Nous avons donc besoin d'un deux-étape d'initialisation ici:std::ref
, non? Peut-être qu'il devrait être appeléstd::rref
.Maintenant, je suppose que ce ne doit pas être de gauche sans être mentionné dans un commentaire 🙂
move_only m[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v(std::make_move_iterator(m), std::make_move_iterator(m + 3));
.Parfois, les solutions les plus simples qui vient de m'échapper. Même si je dois admettre, je ne vous embêtez pas avec ces
move_iterator
s encore.Aussi, pourquoi n'est-ce pas une réponse? 🙂
Je considère qu'un QoI problème, mais je ne vois pas pourquoi il ne pouvait pas le faire. VC++stdlib par exemple la balise dépêches basé sur l'itérateur catégorie et utilise
std::distance
avant-ou-mieux les itérateurs etstd::move_iterator
s'adapte le sous-jacent des itérateurs de la catégorie. De toute façon, la bonne et concis solution. Poster une réponse, peut-être?OriginalL'auteur Xeo
Comme mentionné dans d'autres réponses, le comportement de
std::initializer_list
est de détenir des objets par valeur et ne pas permettre de déménager, donc ce n'est pas possible. Voici une solution de contournement possible, à l'aide d'un appel de fonction où les initialiseurs sont donnés à titre variadic arguments:Malheureusement
multi_emplace(foos, {});
échoue car il ne peut pas déduire le type de{}
, donc pour les objets à défaut construit vous devez répéter le nom de la classe. (ou utilisezvector::resize
)OriginalL'auteur M.M
À l'aide de Johannes Schaub astuce est de
std::make_move_iterator()
avecstd::experimental::make_array()
, vous pouvez utiliser une fonction d'assistance:Voir en direct sur Coliru.
Peut-être quelqu'un peut tirer parti de
std::make_array()
'une ruse pour permettremake_vector()
de faire sa chose directement, mais je ne vois pas comment (avec plus de précision, j'ai essayé ce que je pensais devrait fonctionner, a échoué, et a déménagé sur). Dans tous les cas, le compilateur doit être en mesure d'insérer le tableau de vecteur de transformation, comme Clang ne avec O2 sur GodBolt.OriginalL'auteur metal
Comme il a été souligné, il n'est pas possible d'initialiser un vecteur de mouvement-type uniquement avec une liste d'initialiseur. La solution initialement proposée par @Johannes fonctionne très bien, mais j'ai une autre idée... et si nous n'avons pas créer un tableau temporaire, puis de déplacer les éléments dans le vecteur, mais l'utilisation placement
new
pour initialiser ce tableau déjà en place dans le vecteur de bloc de mémoire?Voici ma fonction pour initialiser un vecteur de
unique_ptr
's à l'aide d'un argument pack:result.data()
n'est pas un pointeur à certains de mémoire vive. Il est un pointeur vers un objet. Pensez à ce qui arrive à ce pauvre objet lorsque vous le placement de nouveau sur elle.En outre, la matrice de la forme de placement de la nouvelle n'est pas vraiment utilisable stackoverflow.com/questions/8720425/...
Martinho Fernandes: merci de remarquer que le placement de nouveau pour les tableaux ne fonctionnerait pas. Maintenant je vois pourquoi c'était une mauvaise idée.
OriginalL'auteur Gart