web-dev-qa-db-fra.com

Gestion de l'avertissement de type noexcept 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 de tous les avertissements activés, comme vous le faites, donne:

$ 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 dois-je faire exactement avec cet avertissement? Quelle est la solution?

32
Barry

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

Désactivez-le avec -Wno-noexcept-type. Dans de nombreux projets, le message d'avertissement est inutile car il n'y a aucune chance que l'objet résultant soit lié à un autre objet qui s'attend à ce qu'il utilise le changement de nom C++ 17 de GCC. Si vous ne compilez pas avec différents paramètres -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 modifié.

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

Lors de l'appel d'un modèle de fonction, spécifiez le paramètre de modèle en indiquant explicitement 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 la conversion 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 pas le mangling.

Lorsque la fonction n'est pas un modèle, n'utilisez pas noexcept avec les types de pointeurs de fonction utilisés dans le type de la fonction. Ceci et le dernier point reposent sur le fait que seuls les types de pointeurs de fonction non-lancement entraînent des changements de dénomination et que des pointeurs de fonction non-lancement peuvent être attribués (C++ 11) ou convertis implicitement (C++ 17) pour éventuellement lancer pointeurs de fonction.

20
Ross Ridge

Je vote la réponse de Ross pour la solution call<void (*)()>(func). Il indique explicitement au compilateur que vous voulez que le modèle soit instancié pour un type de fonction nonnoexcept, et garantit que votre code fonctionnera exactement de la même manière en C++ 17 qu'en C++ 14.

D'autres alternatives sont:

(1) Enveloppez la fonction noexcept dans un lambda (qui n'est pas noexcept):

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

void func() noexcept { }

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

(2) Créez une fonction wrapper séparée sans noexcept. C'est plus de frappe au départ, mais si vous avez plusieurs sites d'appels, cela pourrait économiser la frappe dans l'ensemble. C'est ce que j'ai fini par faire dans le code qui m'a incité à l'origine à déposer le bogue GCC.

3
John Lindgren

En plus de ce qui est déjà 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. Ainsi, une solution serait d'abord d'instancier call() avec une fonction non noexcept.

Cette astuce ferait aussi:

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

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

1
Kane

Le problème qu'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, vous auriez besoin de changer la déclaration de call pour qu'elle soit:

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

Puisque vous avez défini call comme modèle, vous n'avez pas à vous en préoccuper. Cependant, cela pourrait vous poser des problèmes car le type inféré change, ce qui ne se produit 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 et bar sont les mêmes, mais ils sont différents en C++ 17, ce qui signifie que la résolution du modèle échouera. Le message d'erreur dans gcc 7.2 avec l'indicateur -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 pas de problème et vous n'aurez pas de problème de compilation en mode C++ 14 ou C++ 17. Si le code est plus complexe que l'exemple ici (par exemple similaire aux exemples que j'ai donnés ci-dessus), 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 voir s'il y a des avertissements ou des erreurs.

0
SJL