web-dev-qa-db-fra.com

Différence entre "if constexpr ()" Vs "if ()"

Quelle est la différence entre if constexpr() et if()?

Où et quand puis-je les utiliser tous les deux?

27
msc

L'instruction if ordinaire:

  • Son état est-il évalué à chaque fois que le contrôle l'atteint, si jamais
  • Détermine laquelle des deux sous-instructions à exécuter, en ignorant l'autre
  • Exige que les deux sous-instructions soient bien formées, quelle que soit celle réellement sélectionnée lors de l'exécution

L'instruction if constexpr:

  • A sa condition évaluée au moment de la compilation une fois que tous les arguments de modèle nécessaires ont été fournis
  • Détermine laquelle des deux sous-instructions à compiler, en rejetant l'autre
  • Ne nécessite pas que la sous-instruction mise au rebut soit bien formée
17
Brian

La seule différence est que if constexpr Est évalué au moment de la compilation, tandis que if ne l'est pas. Cela signifie que les branches peuvent être rejetées au moment de la compilation et ne seront donc jamais compilées.


Imaginez que vous ayez une fonction, length, qui retourne la longueur d'un nombre ou la longueur d'un type qui a une fonction .length(). Vous ne pouvez pas le faire dans une seule fonction, le compilateur se plaindra:

template<typename T>
auto length(const T& value) noexcept {
    if (std::integral<T>::value) { // is number
        return value;
    else
        return value.length();
}

int main() noexcept {
    int a = 5;
    std::string b = "foo";

    std::cout << length(a) << ' ' << length(b) << '\n'; // doesn't compile
}

Message d'erreur:

main.cpp: In instantiation of 'auto length(const T&) [with T = int]':
main.cpp:16:26:   required from here
main.cpp:9:16: error: request for member 'length' in 'val', which is of non-class type 'const int'
     return val.length();
            ~~~~^~~~~~

C'est parce que lorsque le compilateur instancie length, la fonction ressemblera à ceci:

auto length(const int& value) noexcept {
    if (std::is_integral<int>::value) { // is number
        return value;
    else
        return value.length();
}

value est un int, et en tant que tel n'a pas de fonction membre length, et donc le compilateur se plaint. Le compilateur ne peut pas voir que cette instruction ne sera jamais atteinte pour un int, mais cela n'a pas d'importance, car le compilateur ne peut pas le garantir.

Maintenant, vous pouvez soit spécialiser length, mais pour beaucoup de types (comme dans ce cas - chaque numéro et classe avec une fonction membre length), cela entraîne beaucoup de code dupliqué. SFINAE est également une solution, mais elle nécessite plusieurs définitions de fonctions, ce qui rend le code beaucoup plus long qu'il ne doit être comparé à ce qui suit.

L'utilisation de if constexpr Au lieu de if signifie que la branche (std::is_integral<T>::value) Sera évaluée au moment de la compilation, et si c'est true, alors toutes les autres branches (else if Et else) sont supprimés. Si c'est false, la branche suivante est vérifiée (ici else), et si c'est true, jetez toutes les autres branches, et ainsi de suite ...

template<typename T>
auto length(const T& value) noexcept {
    if constexpr (std::integral<T>::value) { // is number
        return value;
    else
        return value.length();
}

Maintenant, lorsque le compilateur instanciera length, il ressemblera à ceci:

int length(const int& value) noexcept {
    //if (std::is_integral<int>::value) { this branch is taken
        return value;
    //else                           discarded
    //    return value.length();     discarded
}

std::size_t length(const std::string& value) noexcept {
    //if (std::is_integral<int>::value) { discarded
    //    return value;                   discarded
    //else                           this branch is taken
        return value.length();
}

Et donc ces 2 surcharges sont valides, et le code se compilera avec succès.

20
Rakete1111