una significado integrales integracion indefinida ejercicios ejemplos definida define constante calculadora c++ templates c++14 constexpr

c++ - significado - integral definida de una constante



¿Constante integral pasada por valor, tratada como constexpr? (2)

Aunque he usado código como este antes, y está claro que el compilador tiene suficiente información para funcionar, no entiendo por qué compila esto:

template <class T, class I> auto foo(const T& t, I i) { return std::get<i>(t); } int main() { std::cerr << foo(std::make_tuple(3,4), std::integral_constant<std::size_t, 0>{}); return 0; }

Ejemplo en vivo: http://coliru.stacked-crooked.com/a/fc9cc6b954912bc5 .

Parece que funciona con gcc y clang. La constexpr es que si bien integral_constant tiene una conversión constexpr al entero almacenado, las constexpr miembro constexpr toman implícitamente el objeto en sí como un argumento y, por lo tanto, dicha función no se puede usar en un contexto constexpr menos que el objeto al que llamamos la función miembro en sí Puede ser tratado como constexpr .

Aquí, i es un argumento pasado a foo , y por lo tanto, ciertamente no puedo ser tratado como constexpr . Sin embargo, lo es. Un ejemplo aún más simple:

template <class I> void foo(I i) { constexpr std::size_t j = i; }

Esto también se compila, siempre que std::integral_constant<std::size_t, 0>{} se pase a foo .

Siento que me estoy perdiendo algo obvio acerca de las reglas constexpr . ¿Hay una excepción para los tipos sin estado, o algo más? (¿O, quizás, un error de compilación en dos compiladores principales? Este código parece funcionar en Clang 5 y GCC 7.2).

Edición: se ha publicado una respuesta, pero no creo que sea suficiente. En particular, dada la última definición de foo , ¿por qué?

foo(std::integral_constant<std::size_t, 0>{});

Compilar, pero no:

foo(0);

Tanto 0 como std::integral_constant<std::size_t, 0>{} son expresiones constantes.

Edición 2: Parece que se reduce al hecho de que llamar a una función miembro constexpr incluso en un objeto que no es una expresión constante, puede considerarse como una expresión constante, siempre que no se utilice. Esto se está tomando como obvio. No considero esto obvio:

constexpr int foo(int x, int y) { return x; } constexpr void bar(int y) { constexpr auto x = foo(0, y); }

Esto no se compila, porque y como se pasa a foo no es una expresión constante. Está siendo inusitado no importa. Por lo tanto, una respuesta completa debe mostrar algún tipo de lenguaje del estándar para justificar el hecho de que una función miembro constexpr se puede usar como una expresión constante, incluso en un objeto de expresión no constante, siempre que no se use.


La razón por la que esto funciona:

template <class T, class I> auto foo(const T& t, I i) { return std::get<i>(t); }

Es porque ninguna de las razones por las que fallaría se aplicaría. Cuando i es un std::integral_constant<size_t, S> , se puede usar como una expresión constante convertida de tipo size_t porque esa expresión pasa por el constexpr operator size_t() , que simplemente devuelve un parámetro de plantilla (que es un prvalue ) como un valor. Esa es una expresión constante perfectamente válida. Tenga en cuenta que no se hace referencia a esto, simplemente porque es una función miembro que, por sí misma, no viola la restricción de la expresión constante.

Básicamente, no hay nada "runtimey" sobre i aquí.

Por otro lado, si fuera un int (por medio de foo(0) ), llamar std::get<i> implicaría una conversión de lvalue a rvalue para i , pero este caso no cumpliría con ninguno de los criterios, ya que no tengo una inicialización anterior con una expresión constante, no es un literal de cadena, no se definió con constexpr y no comenzó su vida útil en esta expresión.


Las reglas para las expresiones constantes de tiempo de compilación cambiaron con constexpr , MUCHO, pero no son nuevas. Antes de constexpr , ya existían expresiones de tiempo de compilación ... y las reglas antiguas se conservan como casos especiales en la nueva especificación, para evitar romper grandes cantidades de código existente.

En su mayor parte, las reglas antiguas trataban con constantes en tiempo de compilación del tipo integral también conocido como expresión constante ... que es exactamente la situación con la que se está lidiando. Así que no, no hay nada extraño en las reglas de constexpr ... son las otras reglas más antiguas que no tienen nada que ver con constexpr .

Una expresión condicional e es una expresión constante central a menos que la evaluación de e, siguiendo las reglas de la máquina abstracta, evalúe una de las siguientes expresiones:

...

  • una conversión de lvalue a rvalue a menos que se aplique a
    • un valor no volátil de tipo integral o de enumeración que hace referencia a un objeto constante no volátil completo con una inicialización anterior, inicializado con una expresión constante, o
    • un glvalue no volátil que se refiere a un subobjeto de un literal de cadena, o
    • un glvalor no volátil que se refiere a un objeto no volátil de fi nido con constexpr , o que se refiere a un subobjeto no mutable de tal objeto, o
    • un valor no volátil de tipo literal que se refiere a un objeto no volátil cuya vida útil comenzó dentro de la evaluación de e;

Tienes razón en que el tercer subbullet no se aplica. Pero el primero lo hace.

Por lo tanto, tiene una interacción interesante entre las nuevas reglas que permiten que los rendimientos de las funciones sean constantes en tiempo de compilación, dependiendo de las reglas para la evaluación en la máquina abstracta, y el comportamiento legado que permite que los valores integrales sean constantes en tiempo de compilación, incluso si no están marcados como tales. .

Aquí hay un ejemplo rápido de por qué no importa que this sea ​​un argumento implícito. Ser un argumento no significa que un objeto sea evaluado:

constexpr int blorg(bool const flag, int const& input) { return flag? 42: input; } int i = 5; // 5 is an integral constant expression, but `i` is not constexpr int x = blorg(true, i); // ok, `i` was an argument but never evaluated constexpr int y = blorg(false, i); // no way

Para las funciones miembro std::integral_constant , puede considerar *this como i en la función blorg : si la ejecución no lo elimina, está bien que se transmita sin ser una constante de compilación.