3
votes

L'initialisation du vecteur de membre de classe avec la taille échoue

Je suis nouveau avec C ++ et j'ai rencontré ce problème. Voici mon code:

class A
{
    std::vector <int> vec = std::vector<int>(10);
};

Cela me donne une erreur en disant s'attend à un spécificateur de type . Je sais que cette façon d'initialiser un vecteur n'est pas acceptable et qu'elle devrait être effectuée dans le constructeur mais je suis curieux de savoir pourquoi cela échoue. J'ai également remarqué que ceci:

class A
{
    std::vector <int> vec(10);
};

Fonctionne parfaitement bien.

Ma question est donc de savoir pourquoi le deuxième cas fonctionne même si nous sommes toujours en train de créer des vecteurs avec " taille prédéfinie "( std :: vector (10) ), et pourquoi le premier cas échoue-t-il? merci.

P.S J'essaie de créer un vecteur avec une taille de 10 pouces, pas de créer un vecteur avec 10 déjà inséré dedans.


6 commentaires

En effet, le premier extrait de code déclare une fonction appelée vec , qui renvoie std::vector et prend un paramètre de . Je suppose que c'est le double, mais il y a peut-être mieux: Ma tentative d'initialisation de valeur est interprétée comme une déclaration de fonction, et pourquoi pas A a (()); le résoudre?


C ++ 11 ou supérieur? utilisez {20} au lieu de (20) . Vous avez déclaré une fonction qui prend une erreur de syntaxe et renvoie un std::vector .


Merci pour votre réponse. J'essayais de créer un vecteur avec de la mémoire allouée pour 6 membres, est-ce que {20} fera de même?


Ce n'est pas une erreur d'analyse des plus vexantes. Il s'agit d'une définition variable, mais dans un endroit, elle n'est pas autorisée.


@mch Pour vector , car il a un constructeur initializer_list donc ils ne sont pas les mêmes. vector (20) : 20 éléments avec une valeur de 0. vector {20} : un seul élément avec une valeur de 20.


@Yksisarvinen Non, ce n'est pas le cas. Une déclaration de fonction ne peut pas avoir de littéral entier à la place de sa liste d'arguments.


3 Réponses :


8
votes

Quoi?

Depuis C ++ 11, nous avons initialiseurs de membres de données non statiques , rendant cette approche d'initialisation des membres totalement valide. Cependant, vous devez utiliser soit = , soit {}.

Formellement, si un initialiseur est présent ici, il doit être avec une accolade-or-equal -initializer production grammaticale.

Puisque vous avez utilisé à la place une syntaxe qui ne compte pas comme une initialisation ici, le compilateur essaie de faire une déclaration de fonction à partir de vec , ce qui casse car 20 n'est pas un type.


Pourquoi?

Le chapitre intitulé "Problème 1" dans la proposition de fonctionnalité d'origine ( N2756 ) explique que () n'a pas été autorisé ici pour éviter une certaine confusion en ce qui concerne les déclarations des fonctions membres.

L'auteur y note que le comité aurait pu le laisser faire, et le faire fonctionner comme d'autres déclarations - c'est-à-dire que c'est une fonction si cela peut être, et un objet sinon *. Mais l'auteur a décidé d'aller dans une direction différente et d'interdire simplement la syntaxe pour éviter le problème dans ce cas particulier.

Malheureusement, cela rend les initialiseurs de la forme “( liste-d'expressions )” ambigus au moment où la déclaration est analysée [..] < / em>

Une solution possible est de s'appuyer sur la règle existante selon laquelle, si une déclaration peut être un objet ou une fonction, alors c'est une fonction [..]

Une solution similaire serait d’appliquer une autre règle existante, actuellement utilisée uniquement dans les modèles, selon laquelle si T pouvait être un type ou autre chose, alors c’était autre chose; et nous pouvons utiliser « typename » si nous entendons vraiment un type [..

Ces deux solutions introduisent des subtilités susceptibles d'être mal comprises par de nombreux utilisateurs (comme en témoignent les nombreuses questions sur comp.lang.c ++ sur la raison pour laquelle " int i (); < / code> »à la portée du bloc ne déclare pas un int initialisé par défaut.

La solution proposée dans cet article est de n'autoriser que les initialiseurs des formes «= clause d'initialisation » et «{ liste-d'initialisation }».

/ strong> Cela résout le problème d'ambiguïté dans la plupart des cas.

Telle est la vie.


Et alors?

J'essaie de créer un vecteur avec une taille de 10 pouces, pas de créer un vecteur avec 10 déjà insérés.

C'est un point important. Attention, car l'initialisation d'un vecteur avec {} a gagné " t fais ça . L'initialisation "uniforme" malheureusement a simplement ajouté plus de significations . Voilà pour résoudre le problème d'ambiguïté…

Vous devez donc utiliser soit la syntaxe = , soit simplement le faire dans le constructeur (mon préféré).


* C'est là que l'analyse la plus vexante peut parfois vous mordre. toutes les occurrences de déclarations de fonctions involontaires ne sont pas un exemple de l'analyse la plus vexante.


2 commentaires

Merci pour l'explication détaillée :)


@ user3503143 De rien! Je ne savais pas cela, alors j'ai appris quelque chose aussi.



1
votes

Ce n'est pas bien écrit. Vous pouvez également utiliser l'exemple suivant. Ici, je vais laisser le vecteur vide, mais reserve () de l'espace pour 20 pointeurs, donc vous pouvez insérer sans avoir à réallouer. Une solution plus simple et meilleure (avec un constructeur) serait:

A.cpp

Setting size

A.hpp

#include "A.hpp"

int main()
{
   A a;
}

main.cpp

#ifndef A_HPP
#define A_HPP_

#include <iostream>
#include <vector>

class A {
public:
        A();
        ~A();
        std::vector<int> vec;
};

#endif /* !A_HPP_ */

Output:

#include "A.hpp"

A::A()
{
   this->vec.reserve(20);
   std::cout << "Setting size" << std::endl;
}
A::~A()
{
}

Et votre taille de vecteur est faite.


1 commentaires

Ce n'est pas au mauvais endroit, et c'est permis, c'est juste écrit de manière invalide.



0
votes

Vous n'êtes pas autorisé à déclarer et à initialiser une variable membre avec la syntaxe type name (value); , mais vous pouvez avec type name {value}; ou type name = value; .

Voici la syntaxe du Standard:

[class.memedral/1

class C
{
    int a = 0;
    int b{0};
}

Et

[dcl.init /1

brace-or-equal-initializer:
  = initializer-clause
  braced-init-list
braced-init-list:
  { initializer-list , (opt) }
  { designated-initializer-list , (opt) }
  { }

Donc, ce qui suit:

member-declaration:
  attribute-specifier-seq(opt) decl-specifier-seq(opt) member-declarator-list(opt);
  [...]
member-declarator-list:
  member-declarator
  member-declarator-list , member-declarator
member-declarator:
  [...]
  declarator brace-or-equal-initializer(opt)
  [...]

est valide.

La définition de C :: a est une déclaration-membre avec une liste-déclarateur-membre composée d'un seul membre initialisé avec un initialiseur-accolade du form = clause d'initialisation .

De même, C :: b utilise la forme braced-init-list .

D'un autre côté, vous ne trouverez aucune syntaxe pour int c (0); .


0 commentaires