web-dev-qa-db-fra.com

Dois-je mettre constexpr après else-if?

Inspiré par cette réponse , j'ai essayé de copier et coller (et d'ajouter des tests dans main()) ce code:

template<typename T>
std::Tuple<int, double> foo(T a) {
    if constexpr (std::is_same_v<int, T>)
        return {a, 0.0};

    else if (std::is_same_v<double, T>)
        return {0, a};

    else
        return {0, 0.0};
}

int main()
{
    auto [x, y] = foo("");

    std::cout << x << " " << y;
}

C'est très simple - si T est déduit comme int, nous voulons retourner un Tuple de [a, 0.0]. Si T est déduit comme double, nous voulons retourner un Tuple de [0, a]. Sinon, nous voulons retourner [0, 0.0].

Comme vous pouvez le voir, dans la fonction main(), j'appelle foo avec l'argument const char*, Qui devrait résulte en x et y étant 0. C'est pas le cas.

En essayant de le compiler, j'ai rencontré une étrange erreur:

erreur: impossible de convertir "{0, a}" de "<brace-enclosed initializer list>" en "std::Tuple<int, double>"

Et j'étais comme quoi?. Pourquoi diable voudrais-je cela ... J'ai spécifiquement utilisé std::is_same Pour activer return {0, a} uniquement lorsque le type de a est déduit comme double.

J'ai donc rapidement couru vers cppreference sur if-constexpr. Au bas de la page, au-dessus Notes , nous pouvons voir cet extrait de code:

extern int x; // no definition of x required
int f() {
if constexpr (true)
    return 0;
else if (x)
    return x;
else
    return -x;
}

Je me suis dit ouais ..? Je ne peux pas vraiment voir ce qui ne va pas avec le code d'origine. Ils utilisent la même syntaxe et la même sémantique ....

Mais j'étais curieux. J'étais curieux de savoir si quelque chose d'étrange (à ce moment-là) pouvait résoudre ce problème, j'ai donc changé le code d'origine en:

template<typename T>
std::Tuple<int, double> foo(T a) {
    if constexpr (std::is_same_v<int, T>)
        return {a, 0.0};

    else if constexpr (std::is_same_v<double, T>) // notice the additional constexpr here
        return {0, a};

    else
        return {0, 0.0};
}

int main()
{
    auto [x, y] = foo("");

    std::cout << x << " " << y;
}

Et voilà! Le code a été compilé et exécuté comme prévu. Donc, ma question est - Faut-il mettre constexpr après chaque if instruction dans if-else Instruction dans ce genre de situations? Ou est-ce juste mon compilateur? J'utilise GCC 7.3.

34
Fureeish

Avons-nous besoin de mettre constexpr après chaque instruction if dans le bloc if-else dans ce genre de situations?

Oui. Le bloc else-if1 est un mensonge :), il n'y a que des blocs1 et sinon des blocs1. Voici comment votre code est vu par le compilateur:

if constexpr (std::is_same_v<int, T>)
    return {a, 0.0};
else // {
    if (std::is_same_v<double, T>)
        return {0, a};
    else
        return {0, 0.0};
// }

else if (/*...*/) n'est qu'une convention de formatage que tout le monde utilise. En tant que tel, vous pouvez clairement voir que le second constexpr est nécessaire.


1: "bloc" n'est pas la terminologie correcte. if est une instruction (avec une partie else facultative). Un bloc est { /*...*/ }.

46
Rakete1111