11 votes

Initialisation de la structure du modèle

J'essaie de créer un modèle de liste chaînée et cela fonctionne bien pour les types définis par l'utilisateur, mais pour les types fondamentaux comme int, les comportements de gcc et clang diffèrent.

template<class T>
struct Node {
  Node* next;
  T val;
};

template<class T, class... Args>
Node<T> create(Args... args) {
  return {nullptr, {args...}};
}

int main() {
  create<int>(0);
}

Alors que clang compile ce code sans problème, gcc génère le message d'erreur suivant.

error : impossible de convertir '{nullptr, {args#0}}' de '<brace-enclosed initializer list>' en 'Node<int>'.

Bien que je sache comment résoudre ce problème, je suis toujours intéressé de savoir si clang est trop permissif et si je ne peux pas compter sur la portabilité de ce code, ou si c'est un bug de gcc qui devrait être résolu un jour.

Exemple : https://godbolt.org/g/9gnvNQ

4voto

xskxzr Points 5160

C'est un bogue de GCC.

Tout d'abord, les accolades autour de l'initialisateur scalaire (initialisation de liste pour le type scalaire) sont autorisées selon la norme [dcl.init.list]/3.9 :

Sinon, si la liste d'initialisation comporte un seul élément de type E et que soit T n'est pas un type de référence, soit son type référencé est lié à E, l'objet ou la référence est initialisé à partir de cet élément (par copie-initialisation pour l'initialisation par copie de liste, ou par initialisation directe pour l'initialisation par liste directe) ; si une conversion de restriction (voir ci-dessous) est nécessaire pour convertir l'élément en T, le programme est mal formé. [ Exemple :

int x1 {2};                         // OK
int x2 {2.0};                       // error: narrowing

- fin de l'exemple ]

Deuxièmement, Node<int> est un agrégat selon [dcl.init.aggr]/1 :

Un agrégat est un tableau ou une classe avec

  • aucun constructeur fourni par l'utilisateur, explicite ou hérité ([class.ctor]),

  • aucun membre de données non statique privé ou protégé ([class.access]),

  • aucune fonction virtuelle, et

  • aucune classe de base virtuelle, privée ou protégée ([class.mi]).

L'initialisation de l'agrégat est donc effectuée et val est initialisé en liste avec {args...} de manière récursive selon [dcl.init.aggr]/4.2 :

Sinon, l'élément est initialisé par copie à partir de l'élément correspondant. clause d'initialisation ou le initialisateur d'accolade ou d'égalité de l'action correspondante clause d'initialisation désignée . Si cet initialisateur est de la forme Expression de mission ou = Expression de mission et qu'une conversion restrictive est nécessaire pour convertir l'expression, le programme est mal formé. [ Note : Si un initialisateur est lui-même une liste d'initialisateurs, l'élément est initialisé par la liste. ce qui entraînera l'application récursive des règles de ce paragraphe si l'élément est un agrégat. - note de fin ]

Puis [dcl.init.list]/3.9 s'applique à nouveau.

En conclusion, cette initialisation est bien définie.

1voto

yoyoyango Points 140

Je crois que vous voulez quelque chose comme ça :

return {nullptr, T{args...}};

Ceci construit explicitement un objet T en utilisant les arguments fournis et fonctionne également avec n'importe quel type défini par l'utilisateur, comme le montre l'exemple suivant

template<class T>
struct Node {
    Node* next;
    T val;
};

template<class T, class... Args>
Node<T> create(Args... args) {
    return {nullptr, T{args...}};
}

struct Foo {
    string s;
    int i;
};

int main() {
    auto n = create<Foo>("foo", 42);
    cout << n.val.s << ' ' << n.val.i << endl;
}

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