Quels sont les guides de déduction std:: vector en C++17?

J'ai lu sur les guides de déduction pour std::vector en utilisant cppreference .

Exemple:

#include <vector>

int main() {
   std::vector<int> v = {1, 2, 3, 4};
   std::vector x{v.begin(), v.end()}; // uses explicit deduction guide
}

Donc, j'ai quelques questions à ce sujet:

  • Quels sont les guides de déduction std::vector en C++17?

  • Pourquoi et quand avons-nous besoin de déduction vectorielle?

  • Ici, Est - x un std::vector<int> ou std::vector<std::vector<int>>?

22
demandé sur Deduplicator 2017-09-01 11:14:56

3 réponses

Quels sont les guides de déduction std::vector en C++17?

Un guide de déduction défini par l'utilisateur permet aux utilisateurs de décider comment déduction d'argument de modèle de classe déduit les arguments d'une classe de modèle à partir de ses arguments de constructeur. Dans ce cas, il semble que std::vector a un guide explicite qui devrait rendre la construction à partir d'une paire d'itérateurs plus intuitive.


Pourquoi et quand avons-nous besoin de déduction vectorielle?

Nous n'en avons pas besoin, mais ça est utile dans le code générique et dans le code qui est très évident (c'est-à-dire le code où spécifier explicitement les arguments du modèle n'est pas bénéfique pour le lecteur) .


Est x un vector<int> ou vector<vector<int>>?

Voici une bonne astuce pour comprendre cela rapidement-écrire une déclaration de fonction de modèle sans définition et tenter de l'appeler. Le compilateur imprimera le type des arguments passés. Voici ce que g++ 8 imprime:

template <typename> 
void foo();

// ...

foo(x);

Erreur: Aucune fonction correspondante pour l'appel à foo(std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> > ...

Comme vous pouvez le voir dans le message d'erreur, x est déduit de std::vector<std::vector<int>::iterator>.


Pourquoi?

std::vector'les guides de déduction s sont disponibles sur cppreference.org . la norme semble définir un guide de déduction explicite à partir d'une paire d'itérateurs:

entrez la description de l'image ici

Le comportement rencontré dans g++ 8 semble être correct indépendamment, comme (citant Rakete1111)

  • Résolution de Surcharge préfère le constructeur std::initializer_list, avec l'embrassa liste d'initialiseur

  • Les autres constructeurs ne sont considérés qu'après que tous les constructeurs std::initializer_list ont été essayés dans list-initialization

std:vector<std::vector<int>::iterator> est donc le résultat correct lors de l'utilisation de la liste d'initialisation. exemple en direct

Lors de la construction de x avec std::vector x(v.begin(), v.end()), int sera déduit à la place. exemple en direct

18
répondu Vittorio Romeo 2017-09-01 12:54:42

Ici, Est de x un std::vector<int> ou std::vector<std::vector<int>>?

Les autres réponses ici répondent à vos autres questions, mais je voulais aborder celle-ci un peu plus en profondeur. Lorsque nous faisons la déduction d'argument de modèle de classe, NOUS synthétisons un tas de modèles de fonction à partir des constructeurs, puis un peu plus à partir de guides de déduction et effectuons une résolution de surcharge pour déterminer les paramètres de modèle corrects.

Il y a pas mal de constructeurs à std::vector<T,A> , mais la plupart d'entre eux ne mentionnent pas T ce qui ferait de T un contexte non déduit et donc pas une option viable dans cette surcharge. Si nous pré-élaguons l'ensemble pour n'utiliser que ceux qui pourraient être viables:

template <class T> vector<T> __f(size_t, T const& );    // #2
template <class T> vector<T> __f(vector<T> const& );    // #5
template <class T> vector<T> __f(vector<T>&& );         // #6, NB this is an rvalue ref
template <class T> vector<T> __f(initializer_list<T> ); // #8

Et puis aussi ce guide de déduction , que je vais également simplifier en laissant tomber l'allocateur:

template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );

Ce sont nos 5 candidats, et nous surchargeons comme si par [dcl.init], un appel via __f({v.begin(), v.end()}). Parce qu'il s'agit d'une initialisation de liste, nous démarrons avec les initializer_list candidats et, seulement s'il n'y en a pas, nous procédons aux autres candidats. Dans ce cas, il y a un candidat initializer_list qui est viable (#8), donc nous le sélectionnons sans même considérer aucun des autres. Ce candidat déduit T comme std::vector<int>::iterator, nous redémarrons donc le processus de résolution de surcharge pour sélectionner un constructeur pour std::vector<std::vector<int>::iterator> list-initialisé avec deux itérateurs.

Ce n'est probablement pas le résultat souhaité - nous voulions probablement un vector<int>. La solution là bas est simple: utilisez () s:

std::vector x(v.begin(), v.end()); // uses explicit deduction guide

Maintenant, nous ne faisons pas d'initialisation de liste, donc le candidat initializer_list n'est pas un candidat viable. En conséquence, nous déduisons vector<int> à travers le guide de déduction (le seul candidat viable), et finissons par appeler le constructeur de la paire d'itérateurs. Cela a l'avantage secondaire de rendre le commentaire correct.


C'est l'un des nombreux endroits où l'initialisation avec {} est quelque chose de très différent que l'initialisation avec (). Quelque argumenter que {} est une initialisation uniforme-quels exemples comme celui-ci semblent contrer. Ma règle de base: utilisez {} lorsque vous avez spécifiquement besoin consciemment du comportement que {} fournit. () sinon.

7
répondu Barry 2017-09-02 00:12:41

Quels sont les guides de déduction std::vector en C++17?

Déduction D'argument de modèle de classe spécifie :" pour instancier un modèle de classe, chaque argument de modèle doit être connu, mais tous les arguments de modèle ne doivent pas être spécifiés."

Et c'est localisé pour std:vector, je veux dire un std:vector est juste une classe. Rien de spécial à ce sujet.

Voici le guide de déduction std::vector de la ref:

template< class InputIt,
          class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
vector(InputIt, InputIt, Alloc = Alloc())
  -> vector<typename std::iterator_traits<InputIt>::value_type, Alloc>;

Si vous n'êtes pas familier avec la syntaxe, veuillez lire Quels sont les guides de déduction de modèle en C++17?

Pourquoi et quand avons-nous besoin de déduction vectorielle?

Vous avez besoin de guides lorsque la déduction du type des arguments n'est pas basée sur le type de l'un de ces arguments.

Est un vector<int> ou vector<vector<int>>?

Ni l'un ni l'autre!

C'est un:

std::vector<std::vector<int>::iterator>

Forcer une simple erreur de compilation (en attribuant un nombre à x par exemple) dévoilera son type):

error: no match for 'operator=' (operand types are 'std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, std::allocator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > > >' and 'int')

PS:

Pourquoi avons-nous besoin de cette initialisation, Alloc = Alloc() dans ce guide?

C'est un argument par défaut , qui permet de passer un allocateur. Le défaut signifie que vous n'avez pas besoin de deux guides.

5
répondu gsamaras 2017-09-01 16:23:51