2017-10-16 3 views
4

Dans l'exemple suivant main peut static_assert si un littéral de chaîne commence par 'v', mais verify ne peut pas.Comment faire pour affirmer une condition sur un littéral de chaîne à l'intérieur d'une fonction constexpr?

Pourquoi cela se produit-il? Y at-il un moyen d'autoriser verify à static_assert conditions sur les caractères dans un littéral de chaîne?

#include <cstddef> 

template <std::size_t N> 
constexpr char get_first(const char (&str)[N]) 
{ 
    static_assert(N>1, "must be > 1"); 
    return str[0]; 
} 

template <std::size_t N> 
constexpr void verify(const char (&str)[N]) 
{ 
    static_assert(str[0] == 'v', "must start from v"); 
} 

int main() 
{ 
    static_assert(get_first("value") == 'v', "first must be 'v'"); // succeeds 
    verify("value"); // fails to compile 
} 

Erreurs de compilation:

main.cpp: In instantiation of 'constexpr void verify(const char (&)[N]) [with long unsigned int N = 6]': 
main.cpp:19:15: required from here 
main.cpp:13:9: error: non-constant condition for static assertion 
     static_assert(str[0] == 'v', "must start from v"); 
     ^~~~~~~~~~~~~ 
main.cpp:13:9: error: 'str' is not a constant expression 

Example.

+0

Si vous voulez un paramètre de chaîne, vous pouvez la compilation affirment, je suggère Hana. – chris

Répondre

1

Le problème est que verify()

template <std::size_t N> 
constexpr void verify(const char (&str)[N]) 
{ 
    static_assert(str[0] == 'v', "must start from v"); 
} 

peut être appelée compilation et exécution.

Donc, l'erreur, car peut effectuer un static_assert(), sur str, quand appelé à la compilation, mais ne peut pas quand appelé runtime (lorsque le premier caractère de str n'est pas connu à la compilation).

- EDIT -

L'OP demander une vérification de la compilation sur une chaîne littérale.

Ce qui suit est un exemple stupide et je suppose est pas vraiment utile, mais je l'espère, être d'inspiration

template <char const * const str> 
constexpr bool startWithUpperLetter() 
{ 
    static_assert(str[0] != 'v', "no start with \'v\', please"); 

    return (str[0] >= 'A') && (str[0] <= 'Z'); 
} 

constexpr char const str1[] { "ABC" }; 
constexpr char const str2[] { "abc" }; 
constexpr char const str3[] { "vwx" }; 

int main() 
{ 
    static_assert(startWithUpperLetter<str1>() == true, "!"); 
    static_assert(startWithUpperLetter<str2>() == false, "!"); 
    // static_assert(startWithUpperLetter<str3>() == false, "!"); // error 
} 
+0

Existe-t-il des règles de langage qui font que la fonction ou la fonction appelle plus de temps d'exécution que nous ne le pensons ou moins de temps de compilation que nous le pensons? –

+0

@NickyC - pas sûr de comprendre votre question mais ... sans 'constexpr', une fonction est (idéalement) uniquement exécutable; avec 'constexpr' est à l'exécution ** et ** à la compilation activé et je ne connais pas un moyen d'imposer cela est seulement activé à la compilation; Si vous voulez quelque chose qui fonctionne seulement à la compilation, je suppose que vous devez travailler avec la métaprogrammation de template. – max66

+0

@ max66 Existe-t-il un moyen de forcer l'évaluation de 'verify' au moment de la compilation seulement pour que je puisse y ajouter static static? est-il un moyen d'analyser les caractères dans les littéraux de chaîne avec métaprogrammation de modèle? –

2

J'ai une autre solution pour vous. Cela n'utilisera pas static_assert mais garantit l'application de la condition lors de la compilation.

#include <type_traits> 
template<bool b> 
using enforce = std::bool_constant<b>; 

template <std::size_t N> 
constexpr int verify(const char (&str)[N]) 
{ 
    if(get_first(str) != 'v') { 
     throw "must start from v"; 
    } 
    return 0; 
} 

int main() 
{ 
    using assertion = enforce<verify("value")>; // compiles 
    using assertion = enforce<verify("fail")>; // fails to compile 
    // or use it like 
    constexpr auto assertion0 = verify("value"); // compiles 
} 

Ceci utilise que le lancement n'est pas valide dans un contexte constexpr. L'erreur que vous recevrez regardera somethign comme ceci:

26 : <source>:26:31: error: non-type template argument is not a constant expression 
    using assertion = enforce<verify("fail")>; // fails to compile 
           ^~~~~~~~~~~~~~ 
15 : <source>:15:9: note: subexpression not valid in a constant expression 
     throw "must start from v"; 
     ^
26 : <source>:26:31: note: in call to 'verify("fail")' 
    using assertion = enforce<verify("fail")>; // fails to compile 

Nous pouvons appliquer l'évaluation constexpr de verify en l'utilisant comme un argument de modèle. C'est également la raison pour déclarer un type de retour non-nul.

+2

une autre façon est de stocker la valeur de 'verify()' dans une valeur 'constexpr'; par exemple: 'constexpr int vr0 = verify (" value "); constexpr int vr1 = vérifier ("échouer"); '; pas besoin de «forcer», dans ce cas – max66

3

En C++ 17, vous pouvez envelopper la valeur dans un lambda constexpr (online demo). L'appel ressemble à

verify([=] { return "value"; }); 

et déroulez vous pouvez

template <class StringWrapper> 
constexpr void verify(StringWrapper str_w) 
{ 
    constexpr auto str = str_w(); 
    static_assert(str[0] == 'v', "must start from v"); 
}