11 votes

Dans quels cas un compilateur C++ déduit-il noexcept ?

Supposons qu'un compilateur C++ compile une fonction dont la définition est disponible dans la même unité de traduction que son invocation. Supposons qu'elle ne se lance pas elle-même et qu'elle n'appelle pas une fonction dont on sait qu'elle peut se lancer. Supposons également qu'aucun extern C est appelé, ni une division numérique avec une valeur potentiellement nulle.

Dans ces conditions, le compilateur traitera-t-il la fonction comme étant noexcept ? Si ce n'est pas le cas, existe-t-il des conditions supplémentaires dans lesquelles noexcept est déduite ?

Plus précisément, qu'en est-il des fonctions super-simples telles que

void foo() { } /* this one */
class A { 
    int x_; 
public: 
    x() const { return x_; }  /* ... and this one */
}

?

Je voudrais une réponse basée uniquement sur la norme, avant tout, et éventuellement aussi sur ce que font GCC et clang.

5voto

AndyG Points 3298

Presque toutes les fonctions sont supposées être potentiellement lancées, à moins que vous n'utilisiez explicitement une fonction de type noexcept spécificateur. Les exceptions sont pour vos propres définitions de delete (fonctions de désallocation), et certaines fonctions membres spéciales : constructeurs, destructeurs et opérateurs d'affectation. (C++17)

De [except.spec]

Si la déclaration d'une fonction n'a pas d'objet noexcept-specifier la déclaration a un potentiel de lancer à moins qu'il ne s'agisse d'un destructeur ou d'une fonction de désallocation, ou qu'elle soit proposée par défaut lors de sa première déclaration, auquel cas la spécification de l'exception est telle que précisée ci-dessous et aucune autre déclaration pour cette fonction ne doit avoir une noexcept-specifier .

Constructeurs

Sont implicitement noexcept à moins que toute initialisation effectuée pour un membre quelconque (ou un membre d'un membre, etc.) soit susceptible de lancer

Destructeurs

sont implicitement noexcept à moins que tout destructeur d'un sous-objet potentiellement construit soit potentiellement lanceur.

Opérateurs d'assignation

Sont implicitement noexcept sauf si l'utilisation de l'assignation à l'intérieur est potentiellement lancinante.


Voici un exemple de code qui démontre ce qui précède ( clang 6.0.0 , gcc 8.0.0 ) :

int foo() { return 1; }
int bar() noexcept{ return 1; }

struct Foo{};

struct Bar{
 Bar(){}
};

int main()
{
    static_assert(noexcept(bar()));
    static_assert(!noexcept(foo()));
    static_assert(noexcept(Foo()));
    static_assert(noexcept(Foo().~Foo()));
    static_assert(noexcept(Foo().operator=(Foo())));
    static_assert(!noexcept(Bar()));
    Bar b;
    static_assert(noexcept(b.~Bar()));
}

Une raison de plus d'utiliser =default ou permettre au compilateur de générer ses propres versions de vos fonctions membres spéciales par omission.

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