Utilisation du mot-clé C++11 auto pour déclarer deux (ou plus) variables

J'ai un code comme ceci:

template<class ListItem>
static void printList(QList<ListItem>* list)
{
    for (auto i = list->size() - 1, j = -1; i >= 0; --i) {
        std::cout << i << ", " << j << ": " << list->at(i) << std::endl;
    }
}

Lorsque je le compile avec g++ 6.2.1, j'obtiens la sortie du compilateur suivante:

test.cpp: In function ‘void printList(QList<T>*)’:
test.cpp:10:7: error: inconsistent deduction for ‘auto’: ‘auto’ and then ‘int’
  for (auto i = list->size() - 1, j = -1; i >= 0; --i) {
       ^~~~

Je comprendrais cela, si les variables avaient différents types comme auto i = 0.0, j = 0;, mais dans ce cas list est un pointeur vers QList et sa méthode size () renvoie int, -1 seul devrait être int, aussi. Le message d'erreur est un peu étrange aussi.

Les Variables i et j ne sont nécessaires que dans cette boucle et je voudrais les déclarer comme paramètres de boucle. Ce n'est pas difficile de taper int au lieu d'auto, mais j'aimerais savoir: auto n'est-il pas censé être utilisé pour déclarer plusieurs variables en une seule fois, ou il me manque quelque chose ici et c'est vraiment un code erroné, ou peut-être que c'est le bug du compilateur?

P.S. on dirait que l'utilisation d'une fonction de modèle est la partie critique ici, factoriser la boucle hors du modèle ne produit pas d'erreurs. Donc, plus comme un bug dans le compilateur?

Démo en direct-code minimal

25
demandé sur Oktalist 2016-11-28 13:16:42

3 réponses

C'est un bug dans GCC.

Selon [dcl.specs.auto] / 1:

Le auto et decltype(auto) type de spécificateurs de sont utilisés pour désigner un type d'espace réservé qui sera remplacé plus tard par déduction d'un initialiseur. [...]

Les règles de déduction d'argument de modèle ne déduisent jamais qu'un type est auto. Le but de la déduction dans ce cas est en fait de remplacer auto avec un type déduit.

Dans l'exemple, list est un type dépendant (cela dépend du paramètre template ListItem), donc l'expression list->size() - 1 a également un type dépendant, ce qui rend le type de i également dépendant, ce qui signifie qu'il ne sera résolu qu'à l'instanciation du modèle de fonction printList. Alors seulement les autres contraintes sémantiques liées à cette déclaration, être vérifiée.

Selon [temp.res] / 8:

Savoir quels noms sont des noms de type permet la syntaxe de chaque modèle pour être vérifié. Le programme est mal formé, aucun diagnostic requis, si:

[... longue liste de cas, dont aucun ne s'applique ici ...]

Sinon, aucun diagnostic ne doit être délivré pour un modèle pour lequel un une spécialisation valide peut être générée . [ Remarque: Si un modèle est instanciées, les erreurs seront diagnostiquées selon les autres règles dans cette norme. Exactement quand ces erreurs sont diagnostiquées est une qualité de problème de mise en œuvre. - note de fin ]

(soulignement du mien)

GCC a tort d'émettre cette erreur lors de l'analyse de la définition du modèle printList, car des spécialisations clairement valides du modèle peuvent être générées. En fait, si QList n'ont pas de spécialisations pour lesquelles size() renvoie autre chose que int, la déclaration de i et j sera valable dans tous les instanciations de printList.


Toutes les citations proviennent de N4606 , le brouillon de travail (presque) actuel, mais le les parties pertinentes des citations ci-dessus n'ont pas changé depuis C++14.


Update: confirmé comme une régression {[55] } dans GCC 6 / 7. Merci à T. C. pour le rapport de bogue.

Mise à jour: le bogue d'origine(78693) a été corrigé pour les prochaines versions 6.4 et 7.0. Il a également découvert d'autres problèmes avec la façon dont GCC gère de telles constructions, entraînant deux autres rapports de bogues: 79009 et 79013.

13
répondu bogdan 2017-01-18 19:36:51

Tel Que mentionné dans mon commentaire à votre réponse, je suis d'accord avec l'analyse que vous avez présenté.
Forme la plus simple du problème (démo):

template<class T>
void foo (T t) {
  auto i = t, j = 1; // error: inconsistent deduction for ‘auto’: ‘auto’ and then ‘int’
}    
int main () {}

Dans le cas des templates, le compilateur dans sa 1ère étape, vérifie la syntaxe de base sans l'instancier. Dans notre cas, nous n'invoquons jamais foo() de toute façon.

Maintenant, dans l'exemple ci-dessus, le decltype(auto) pour i est toujours auto, car le type dépendant T n'est pas connu. Toutefois, j est sûrement int. Par conséquent, l' l'erreur du compilateur a du sens . Comportement actuel (g++ > = 6), peut ou non être un bug. Cela dépend de ce que nous attendons du compilateur. :-)

Cependant, cette erreur ne peut être condamnée. Voici la citation standard de C++17 draft:

7.1.7.4.1 déduction de type D'espace réservé

4Si l'espace réservé est le spécificateur de type automatique, le type déduit t remplaçant T est déterminé à l'aide de l'argument rules for template déduction. Obtenir P de T en remplaçant les occurrences de auto par un nouveau inventé type Modèle paramètre U

La même chose est présente dans C++14 standard {[13] } comme 7.1.6.4 / 7.


Pourquoi cette erreur est-elle signalée dans la première vérification du modèle lui-même?

Nous pouvons à juste titre argumenter que, pourquoi le compilateur est si "pédant" dans le premier contrôle de syntaxe lui-même. Puisque, nous n'instancions pas, alors ne devrait - il pas être ok! Même si nous instancions, ne devrait-il pas donner une erreur seulement pour les appels problématiques!
C'est ce que g++-5 fait. Pourquoi ont-ils pris la peine de changer?

Je pense que c'est un argument valable. Avec G++-5, si j'appelle:

foo(1);  // ok
foo(1.0); // error reported inside `foo()`, referencing this line

Ensuite, le compilateur signale correctement l'erreur et sa hiérarchie lorsque i et j sont de types différents.

4
répondu iammilind 2017-05-23 12:09:27

Je vais summerise les informations reçues sur le sujet alors.

Le problème dans l'exemple de code est en utilisant une fonction de modèle. Le compilateur effectue d'abord une vérification Générique d'un modèle sans l'instancier, cela signifie que les types, qui sont des arguments de modèle (et les types qui en dépendent, comme les autres modèles) ne sont pas connus, et auto si cela dépend de ces types inconnus est déduit dans auto à nouveau (ou non déduit dans un type concret). Il ne m'est jamais apparu que même après la déduction auto peut toujours être auto. Maintenant, le texte d'erreur du compilateur original a un sens parfait: la variable j est déduite comme étant de type int, mais la variable i est toujours auto après déduction. Puisque auto et int sont de types différents, le compilateur génère l'erreur.

1
répondu drongo 2016-11-28 19:32:37