Lorsque nous voulons utiliser un static_assert
dans un, if constexpr
nous devons rendre la condition dépendante d'un paramètre de modèle. Fait intéressant, gcc et clang ne sont pas d'accord lorsque le code est enveloppé dans un lambda.
Le code suivant se compile avec gcc, mais clang déclenche l'assertion, même si le if constexpr
ne peut pas être vrai.
#include <utility>
template<typename T> constexpr std::false_type False;
template<typename T>
void foo() {
auto f = [](auto x) {
constexpr int val = decltype(x)::value;
if constexpr(val < 0) {
static_assert(False<T>, "AAA");
}
};
f(std::integral_constant<int, 1>{});
}
int main() {
foo<int>();
}
Il peut facilement être corrigé en remplaçant False<T>
par False<decltype(x)>
.
La question est donc: quel compilateur a raison? Je suppose que gcc est correct car la condition dans le static_assert
dépend de T
, mais je ne suis pas sûr.
c++
templates
language-lawyer
c++17
static-assert
florestan
la source
la source
static_assert(False<int>, "AAA");
équivaut à l'static_assert(false, "AAA");
intérieur du lambda.f(std::integral_constant<int, 1>{});
Wandbox ne déclenche pas l' assertionRéponses:
Extrait de [stmt.if] / 2 (c'est moi qui souligne)
Lire que l'on penserait que l'assertion statique serait abandonnée, mais ce n'est pas le cas.
L'assertion statique est déclenchée dans la première phase du modèle car le compilateur sait que c'est toujours faux.
De [temp.res] / 8 (c'est moi qui souligne)
Oui en effet, votre
False<T>
dépendT
. Le problème est qu'un lambda générique est lui-même un modèle etFalse<T>
ne dépend d'aucun paramètre de modèle du lambda.Pour un
T
quiFalse<T>
est faux, l'assertion statique sera toujours fausse, quel que soit l'argument modèle envoyé au lambda.Le compilateur peut voir que pour toute instanciation du modèle
operator()
, l'assertion statique se déclenchera toujours pour le T. actuel. D'où l'erreur du compilateur.Une solution pour cela serait de dépendre
x
:Exemple en direct
la source
La règle habituelle ici est [temp.res] / 8 :
Une fois que vous instanciez
foo<T>
, le questatic_assert
vous avez n'est plus dépendant. Il devientstatic_assert(false)
- pour toutes les instanciations possibles de l'opérateur d'appel du lambda génériquef
. C'est mal formé, aucun diagnostic requis. Clang diagnostique, gcc non. Les deux sont corrects.Notez qu'il n'a pas d'importance que l'
static_assert
ici soit jeté.Cela maintient la
static_assert
dépendance au sein du lambda générique, et maintenant nous entrons dans un état où il pourrait y avoir hypothétiquement une spécialisation valide, donc nous ne sommes plus mal formés, ndr.la source