27 votes

En utilisant std::array et à l'aide de "tableau" que le nom

Dans mon C++ librairie JSON, j'ai récemment eu une régression avec GCC7. J'ai démonté le code affecté et l'espoir de comprendre l'erreur.

Le code

Considérer cet en-tête myclass.hpp:

#pragma once

template <typename X>
struct A
{
    struct value_t
    {
        X array;
    };

    static A array()
    {
        return A();
    }

    friend bool operator<(const A& lhs, const A& rhs) noexcept
    {
        return lhs.val.array < rhs.val.array;
    }

    value_t val = {};  
};

Comme vous le voyez, j'ai utilisé le nom de "tableau" comme nom de variable membre struct value_t, comme le nom d'une fonction statique. J'ai alors compris l'en-tête dans le fichier suivant:

#include <array>
using std::array; // note this!
#include "myclass.hpp"

int main()
{}

Le problème

Le code se compile avec GCC6 et Clang5 (à l'aide d' -std=c++11), mais GCC7 rapports:

In file included from example.cpp:3:0:
myclass.hpp: In function 'bool operator<(const A<X>&, const A<X>&)':
myclass.hpp:19:40: error: wrong number of template arguments (1, should be 2)
         return lhs.val.array < rhs.val.array;
                                        ^~~~~
In file included from example.cpp:1:0:
/usr/local/Cellar/gcc/7.1.0/include/c++/7.1.0/array:94:12: note: provided for 'template<class _Tp, long unsigned int _Nm> struct std::array'
     struct array
            ^~~~~
make: *** [all] Error 1

Il me semble que si l'analyseur lit le "tableau" en lhs.val.array comme std::array et traite les suivantes < comme le début d'une liste de modèle.

Le code peut être compilé si je fais les modifications ci-dessous:

  • Supprimer l' using std::array; ou déplacer derrière #include "myclass.hpp".
  • Variation return lhs.val.array < rhs.val.array; de return (lhs.val.array) < rhs.val.array;.

En outre, le compilateur ne parvient pas, si je retire l' static A array() de la fonction...

Mes questions

  • Le code est-il correct en premier lieu? Suis-je autorisé à utiliser le "tableau" comme un nom, même si j'utilise using std::array;?
  • Si le code est correct, est-ce un bug GCC7?

2voto

Marek Vitek Points 1311

Je n'ai pas trouvé quelque chose qui dit que le comportement-vous découvert est OK, mais j'ai trouvé la suite, qui pourrait affirmer le contraire.

Lorsque le nom d'un membre de la spécialisation de modèle apparaît après . ou -> dans un postfix-expression ou après qu'un sous-nom-spécificateur qualifiée-id, et l'objet de l'expression de la postfix-l'expression est en fonction du type ou de la nested-nom-rédacteur de devis dans la qualifié-id se réfère pour un type de charge, mais le nom n'est pas un membre de l'actuel l'instanciation (14.6.2.1), le membre nom du modèle doit être préfixé par le modèle de mot clé. Sinon le nom est présumé nom d'un non modèle.

[ Example:
struct X {
template<std::size_t> X* alloc();
template<std::size_t> static X* adjust();
};
template<class T> void f(T* p) {
T* p1 = p->alloc<200>(); // ill-formed: < means less than
T* p2 = p->template alloc<200>(); // OK: < starts template argument list
T::adjust<100>(); // ill-formed: < means less than
T::template adjust<100>(); // OK: < starts template argument list
}
- end example ]

Vous pouvez travailler autour de l'id comme l'a déjà suggéré de mettre en comparaison les éléments entre parenthèses. Il va se casser nom array<

    return (lhs.val.array) < (rhs.val.array);

Nous allons vous simplifier le code un peu plus et supprimer toutes les comprend, qui pourrait être à occulter ce qui se passe. Je vais commencer avec le code d'origine n'est toujours pas à compiler.

#include <cstddef> // needed for size_t
//using std::array; brings following two lines into your code:
template< class T, std::size_t N >
struct array;

template <typename X>
struct A
{
    struct value_t { int array; };
    value_t val = {};  
    friend bool operator<(const A& lhs, const A& rhs) {
        return (lhs.val.array < rhs.val.array);
    }
};

Et maintenant passons struct value_t { int array; }; à l'extérieur basé sur un modèle de définition:

#include <cstddef> // needed for size_t
//using std::array; brings following two lines into your code:
template< class T, std::size_t N >
struct array;

struct value_t { int array; };

template <typename X>
struct A
{
    value_t val = {};  
    friend bool operator<(const A& lhs, const A& rhs) {
        return (lhs.val.array < rhs.val.array);
    }
};

Si par dont <array> dans votre code, vous avez introduit le modèle de tableau dans votre code comme indiqué ici. Dans la version avec value_t à l'extérieur du modèle, il est array<T> et membre de l' array. Ce sont des choses différentes et donc sans aucun conflit.
Lorsque vous mettez value_t à l'intérieur du modèle, le compilateur commence ses tentatives pour développer ce qui vient à partir de ce modèle. Il essaie de le faire avec les membres de tableau qui ne devrait pas arriver, comme spécifié dans la norme.

De toute façon, ça ressemble à un bug de GCC, parce que quand il apparaît dans l'expression lhs.val.array il devrait être traité comme modélisé uniquement lorsque le préfixe avec le mot-clé modèle lhs.val.template array<

Et oui, en utilisant le même nom dans différents contextes est parfaitement correct, sauf si c'est l'un des mots réservés, tableau n'est pas. Mais être prudent avec cette utilisation de noms. Je le trouve à moins déroutant à utiliser le nom de matrice pour la variable contenant un unique entier. Le nom le suggère déjà, il y aura plus d'un.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X