34 votes

Gérer l'avertissement noexcept-type de gcc

Considérez cet exemple, tiré de bug 80985 :

template <class Func>
void call(Func f)
{
    f();
}

void func() noexcept { }

int main()
{
    call(func);
}

La compilation avec tous les avertissements activés, comme vous le faites, produit :

$ g++ -std=c++14 -Wall foo.cxx 
foo.cxx:2:6: warning: mangled name for ‘void call(Func) [with Func = void (*)() noexcept]’ will change in C++17 because the exception specification is part of a function type [-Wnoexcept-type]
 void call(Func f)
      ^~~~

Que suis-je censé faire exactement avec cet avertissement ? Quelle est la solution ?

2 votes

Si call est entièrement interne à votre projet, cela n'a pas d'importance. Il n'a d'importance que dans les cas où deux unités de traduction différentes l'utilisent, où l'une a été compilée avec C++17 et l'autre non. Même dans ce cas, puisque call est une fonction modèle, elle n'aura probablement pas un grand impact autre que celui d'avoir une définition supplémentaire dans l'exécutable final.

2 votes

@DanielH Je ne veux pas parler pour Barry, ci-dessus, mais si vous compilez un projet avec -wError, alors cet "avertissement inoffensif" fera que le programme ne compilera pas du tout, même s'il est correct. C'est important.

1 votes

@markt1964 Dans le poste seulement -Wall est utilisé. Si vous compilez avec -Werror ou essayez d'éviter les erreurs de compilation (ce qui est une bonne idée), alors oui, vous aurez un problème. Un problème qui pourrait, peut-être, être mieux résolu en ajoutant -Wno-noexcept-type selon les circonstances.

21voto

Ross Ridge Points 5534

Il y a plusieurs choses que vous pouvez faire à propos du message d'avertissement.

Désactivez-la avec -Wno-noexcept-type . Dans de nombreux projets, le message d'avertissement n'est pas utile car il n'y a aucune chance que l'objet résultant soit lié à un autre objet qui s'attend à ce qu'il utilise la gestion des noms C++17 de GCC. Si vous ne compilez pas avec différents -std= et que vous ne construisez pas une bibliothèque statique ou partagée où la fonction incriminée fait partie de son interface publique, le message d'avertissement peut être désactivé en toute sécurité.

Compilez tout votre code avec -std=c++17 . Le message d'avertissement disparaîtra car la fonction utilisera le nouveau nom tronqué.

Faire la fonction static . Étant donné que la fonction ne peut plus être référencée par un autre fichier objet utilisant une manipulation différente de la fonction, le message d'avertissement ne sera pas affiché. La définition de la fonction devra être incluse dans toutes les unités de compilation qui l'utilisent, mais pour les fonctions modèles comme dans votre exemple, cela est courant de toute façon. De même, cela ne fonctionnera pas pour les fonctions membres qui étaient static signifie autre chose.

Lors de l'appel d'un modèle de fonction, spécifiez explicitement le paramètre du modèle en donnant un type de pointeur de fonction compatible qui n'a pas la spécification d'exception. Par exemple call<void (*)()>(func) . Vous devriez également pouvoir utiliser cast pour faire cela, mais GCC 7.2.0 génère toujours un avertissement même si l'utilisation de -std=c++17 ne change rien à l'affaire.

Lorsque la fonction n'est pas un modèle, il ne faut pas utiliser noexcept avec tout type de pointeur de fonction utilisé dans le type de la fonction. Ce point, ainsi que le dernier, repose sur le fait que seuls les types de pointeurs de fonction non lancés entraînent des changements de nom et que les pointeurs de fonction non lancés peuvent être assignés (C++11) ou implicitement convertis (C++17) en pointeurs de fonction éventuellement lancés.

2voto

John Lindgren Points 465

Je vote en faveur de la réponse de Ross pour le call<void (*)()>(func) solution. Elle indique explicitement au compilateur que vous souhaitez que le modèle soit instancié pour un élément non noexcept et garantit que votre code fonctionnera exactement de la même manière en C++17 qu'en C++14.

D'autres possibilités existent :

(1) Enveloppez le noexcept dans une lambda (qui n'est pas noexcept ):

template <class Func>
void call(Func f)
{
    f();
}

void func() noexcept { }

int main()
{
    call([]() { func(); });
}

(2) Créer une fonction wrapper séparée sans noexcept . Au départ, il faut taper davantage, mais si vous avez plusieurs sites d'appel, cela peut vous permettre d'économiser de la frappe. C'est ce que j'ai fini par faire dans le code qui m'a incité à déposer le bug de GCC.

1voto

Kane Points 1525

En plus de ce qui a déjà été dit, j'ai trouvé un autre moyen de se débarrasser de cet avertissement dans GCC 7. Apparemment, GCC génère cet avertissement si et seulement si la première instanciation de call() implique noexcept . Une solution serait donc d'abord d'instancier call() avec un non noexcept fonction.

Cette astuce ferait aussi l'affaire :

using dummy = decltype(call(std::declval<void(*)()>()));

P.S. GCC 8.2.1 ne signale pas d'avertissement dans ce cas.

0voto

SJL Points 378

Le problème dont ils vous avertissent est qu'en C++14, cela fonctionnera :

void call(void (*f)())
{
    f();
}

void func() noexcept {}

int main(int argc, char* argv[])
{
    call(&func);
    return 0;
}

mais en C++17, il faudrait changer la déclaration de call pour être :

void call(void (*f)() noexcept)
{
    f();
}

Puisque vous avez défini call pour être un modèle, vous n'avez pas besoin de vous inquiéter à ce sujet. Néanmoins, cela pourrait vous poser des problèmes parce que le type inféré change, ce qui n'arrive généralement pas.

Par exemple, ce code sera compilé en C++14 mais pas en C++17 :

void foo() noexcept {}
void bar()          {}

template <typename F>
void call(bool b, F f1, F f2)
{
    if (b)
        f1();
    else
        f2();
}

void foobar(bool b)
{
    call(b, &foo, &bar);
}

En C++14, les types de foo y bar sont les mêmes, mais ils sont différents en C++17, ce qui signifie que la résolution des modèles échouera. Le message d'erreur dans gcc 7.2 avec le drapeau -std=c++1z est :

note:   template argument deduction/substitution failed:
note:   deduced conflicting types for parameter 'F' ('void (*)() noexcept' and 'void (*)()')

Dans l'exemple que vous avez donné, il n'y a aucun problème et vous n'aurez aucun problème à compiler en mode C++14 ou C++17. Si le code est plus complexe que l'exemple donné ici (par exemple, similaire aux exemples que j'ai donnés plus haut), vous pourriez rencontrer des problèmes de compilation. Il semble que vous ayez un compilateur récent ; essayez de compiler avec -std=c++1z et voyez s'il y a des avertissements ou des erreurs.

1 votes

Cela ne répond pas à la question. Je suis au courant de l'ajout de noexcept au système de type.

0 votes

J'ai ajouté plus d'explications à la fin ; j'espère que cela clarifie la question.

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