Jeter dans constexpr fonction

Le morceau de code suivant compile sous clang++ 3.7.0 mais est refusé par g++ 5.3.1. Les deux ont l'option -std=c++14. Le compilateur est correcte? Quelqu'un sait où dans la norme en parle? Grâce.

#include <stdexcept>
using namespace std;

constexpr int f(int n) {
  if (n <= 0) throw runtime_error("");
  return 1;
}

int main() {
  char k[f(1)];
}

Sortie

[hidden] g++ -std=c++14 c.cpp 
c.cpp: In function ‘constexpr int f(int)’:
c.cpp:7:1: error: expression ‘<throw-expression>’ is not a constant-expression
 }
 ^
[hidden] clang++ -std=c++14 c.cpp 
[hidden] 
[hidden] g++ -v
Using built-in specs.
COLLECT_GCC=/usr/bin/g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/5.3.1/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --disable-libgcj --with-isl --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 5.3.1 20151207 (Red Hat 5.3.1-2) (GCC) 
[hidden] 
[hidden] clang++ -v
clang version 3.7.0 (http://llvm.org/git/clang.git 2ddd3734f32e39e793550b282d44fd71736f8d21)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/3.4.6
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/5.3.1
Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/5.3.1
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64
23
demandé sur Shafik Yaghmour 2015-12-15 06:38:58

2 réponses

Clang est correct, notez que la révision HEAD de gcc accepte accepte également ce code. Il s'agit d'une fonction constexpr bien formée, tant qu'il existe une valeur pour les arguments qui permet d'évaluer la fonction en tant qu'expression constante de base. Dans votre cas, 1 est une telle valeur.

Ceci est couvert dans le projet de section standard C++14 7.1.5 le spécificateur constexpr [dcl.constexpr] qui nous dit ce qui est autorisé dans une fonction constexpr:

Le la définition d'une fonction constexpr doit satisfaire aux contraintes suivantes:

  • Il ne doit pas être virtuel (10.3);

  • Son type de retour doit être un type littéral;

  • Chacun de ses types de paramètres doit être un type littéral;

  • Son corps de fonction doit être = delete, = default, ou une instruction composée qui ne contient pas

    • Une définition asm,

    • Un goto déclaration,

    • Essayer de bloquer ou d'

    • Définition d'une variable de type non littéral ou de durée de stockage statique ou de thread ou pour laquelle aucune initialisation n'est effectuée.

Aucune restriction sur throw et il dit aussi (emphase mine):

, Pour un non-modèle, non par défaut constexpr fonction ou d'un non-modèle, non par défaut, non hériter constructeur constexpr, si non les valeurs d'argument existent de telle sorte qu'une invocation de la fonction ou du constructeur peut être une sous-expression évaluée d'une expression constante de base (5.19), le programme est mal formé ; Non de diagnostic nécessaires.

Et en dessous de ce paragraphe, nous avons l'exemple suivant, similaire au vôtre:

constexpr int f(bool b)
  { return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required

throw est pas autorisé en une base constante de l'expression, qui est couvert dans la section 5.19 [expr.const] paragraphe 2, qui dit:

Une expression conditionnelle e est une expression constante de base à moins que l'évaluation de e, suivant les règles de la abstract machine (1.9), évaluerait l'une des expressions suivantes

Et comprend la puce suivante:

  • une expression de projection (15.1).

Et donc f ne seraient pas utilisables dans une expression constante de base quand n <= 0.

mise à Jour

Comme le souligne TemplateRex, il y a deux gcc rapports de bogues pour ceci:

TemplateRex note également que les correctifs ne sont pas appliqués à to 5.3.0 et ne sont que dans le tronc. Non, des solutions de contournement sont fournies.

19
répondu Shafik Yaghmour 2015-12-18 17:57:05

Comme le montre Shafik Yaghmour c'est un bug gcc, qui sera, espérons-le, corrigé dans v6.

Jusque-là, vous pouvez revenir au style c++11 constexpr:

constexpr auto foo(int n) -> int
{
  return n <= 0 ? throw runtime_error("") : 1;
}

Cependant, il existe une meilleure solution de contournement , conservant toujours toutes les extensions c++14 constexpr:

// or maybe name it
// throw_if_zero_or_less
constexpr auto foo_check_throw(int n) -> void
{  
  n <= 0 ? throw std::runtime_error("") : 0;
}

constexpr auto foo(int n) -> int
{
  foo_check_throw(n);

  // C++14 extensions for constexpr work:
  if (n % 2)
    return 1;
  return 2;
}
4
répondu bolov 2017-10-20 05:41:31