¿Necesito poner constexpr después de else-if?


Inspirado por esta respuesta , traté de copiar y pegar (y agregar pruebas en main()) este código:

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;
}

Esto es muy sencillo: si T se deduce como int, queremos volver una tupla de [a, 0.0]. Si T se deduce como double, queremos devolver una tupla de [0, a]. De lo contrario, queremos devolver [0, 0.0].

Como puedes ver, en la función main(), estoy llamando a foo con el argumento const char*, que debería resultar en x y y siendo 0. Que es no el caso.

Al intentar compilarlo, me encontré con un extraño error:

Error: no se pudo convertir '{0, a} ' de '<brace-enclosed initializer list> ' a 'std::tuple<int, double>'

Y yo estaba como ¿qué?. ¿Por qué demonios querría eso?.. Usé específicamente std::is_same para habilitar return {0, a} solo cuando el tipo de a se deduce como double.

Así que rápidamente corrí a cppreference en if-constexpr. En la parte inferior de la página, arriba Notas, podemos ver este fragmento de código:

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

Pensé para mí mismo oookay..? Realmente no puedo ver lo que está mal con el código original. Utilizan la misma sintaxis y semántica....

, Pero tenía curiosidad. Tenía curiosidad si tal vez algo extraño (en ese momento) podría solucionar ese problema, así que cambié el código original a:

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;
}

Y voilà! El código compilado y ejecutado como se esperaba. Entonces, mi pregunta es - Necesitamos poner constexpr después de cada if declaración en if-else declaración en este tipo de situaciones? ¿O es solo mi compilador? Estoy usando GCC 7.3.

Author: Fureeish, 2018-09-16

1 answers

¿Necesitamos poner constexpr después de cada sentencia if en el bloque if-else en este tipo de situaciones?

Sí. El bloque else-if1 es una mentira :), solo hay si los bloques1 y más bloques1. Así es como tu código es visto por el compilador:

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 (/*...*/) es solo una convención de formato que todo el mundo utiliza. Como tal, se puede ver claramente que el segundo constexpr es necesario.


1: "bloque" no es correcta terminología. if es una instrucción (con parte opcional else). Un bloque es { /*...*/ }.

 37
Author: Rakete1111,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-18 04:26:54