¿Constexpr implica en línea?

105

Considere la siguiente función en línea:

// Inline specifier version
#include<iostream>
#include<cstdlib>

inline int f(const int x);

inline int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

y la versión equivalente constexpr:

// Constexpr specifier version
#include<iostream>
#include<cstdlib>

constexpr int f(const int x);

constexpr int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

Mi pregunta es: ¿el constexprespecificador implica el inlineespecificador en el sentido de que si se pasa un argumento no constante a una constexprfunción, el compilador intentará inlinela función como si el inlineespecificador se pusiera en su declaración?

¿El estándar C ++ 11 garantiza eso?

Vincent
fuente
5
"[¿Intentará] el compilador alinear la función" no es lo que hace el inlineespecificador. (O tal vez entendí mal tu fraseo.)
Luc Danton
5
El inlineespecificador ya no tiene nada que ver con el inlining
K-Ballo
2
La pregunta se basa en la suposición incorrecta que inlineestá directamente relacionada con el inlining. Entonces no, el constexprespecificador no implica el inlineespecificador en ese sentido, ya que ese sentido no existe.
Christian Rau

Respuestas:

139

Sí ([dcl.constexpr], §7.1.5 / 2 en el estándar C ++ 11): "las funciones constexpr y los constructores constexpr están implícitamente en línea (7.1.2)".

Sin embargo, inlinetenga en cuenta que el especificador realmente tiene muy poco efecto (si es que tiene alguno) sobre si es probable que un compilador expanda una función en línea o no. Sin embargo, sí afecta la regla de una definición, y desde esa perspectiva, el compilador debe seguir las mismas reglas para una constexprfunción comoinline función.

También debo agregar que independientemente de constexprlo que implica inline, las reglas paraconstexpr funciones en C ++ 11 requerían que fueran lo suficientemente simples como para que a menudo fueran buenos candidatos para la expansión en línea (la excepción principal son las que son recursivas). Desde entonces, sin embargo, las reglas se han vuelto progresivamente más flexibles, por lo que constexprpueden aplicarse a funciones sustancialmente más grandes y complejas.

Jerry Coffin
fuente
Dado que la idea es que las expresiones constantes se evalúan en tiempo de compilación, supongo que la mayoría de los usos de constexprfunciones no causarán generación de código en absoluto ...
Kerrek SB
11
Las constexprfunciones de @KerrekSB se evalúan potencialmente en tiempo de compilación. Sin embargo, el estándar C ++ 14 está plagado de algunos que muy probablemente se llamarán en tiempo de ejecución. Por ejemplo:std::array<T,N>::at
Epónimo
@Eponymous sí, pero solo la forma más reducida permanecerá como códigos de operación. por ejemplo: las comprobaciones vinculadas se evaluarán en el momento de la compilación, ya que su ruta de código es constante. Pero el valor devuelto será * (datos + desplazamiento)
v.oddou
16

constexprno implica inlinepara variables no estáticas (variables en línea de C ++ 17)

Si bien constexprimplica inlinepara funciones, no tiene ese efecto para variables no estáticas, considerando las variables en línea de C ++ 17.

Por ejemplo, si toma el ejemplo mínimo que publiqué en: ¿Cómo funcionan las variables en línea? y elimine el inline, dejando solo constexpr, entonces la variable obtiene múltiples direcciones, que es lo principal que evitan las variables en línea.

constexpr Sin embargo, las variables estáticas son implícitamente estáticas.

Ejemplo mínimo que constexprimplica inlinepara funciones

Como se menciona en: https://stackoverflow.com/a/14391320/895245, el efecto principal de inlineno es inline sino permitir múltiples definiciones de una función, cita estándar en: ¿Cómo puede un archivo de encabezado C ++ incluir la implementación?

Podemos observar eso jugando con el siguiente ejemplo:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    assert(shared_func() == notmain_func());
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline int shared_func() { return 42; }
int notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

int notmain_func() {
    return shared_func();
}

Compilar y ejecutar:

g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'notmain.o' 'notmain.cpp' 
g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.o' 'main.cpp' 
g++ -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.out' notmain.o main.o
./main.out

Si eliminamos inlinede shared_func, el enlace fallaría con:

multiple definition of `shared_func()'

porque el encabezado se incluye en varios .cpparchivos.

Pero si reemplazamos inlinecon constexpr, entonces funciona de nuevo, porque constexprtambién implica inline.

GCC implementa eso al marcar los símbolos como débiles en los archivos de objeto ELF: ¿Cómo puede un archivo de encabezado C ++ incluir la implementación?

Probado en GCC 8.3.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
3
Por cierto, una variable miembro de clase estática declarada constexprtodavía está en línea. cppreference.com : una variable miembro estática (pero no una variable de ámbito de espacio de nombres) declarada constexpres implícitamente una variable en línea.
anton_rh
@anton_rh gracias, no había visto esa regla, actualiza la respuesta.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
no es lo que dice open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf . dice que constexpr implica en línea para las variables. sin mencionar una diferencia entre el ámbito del espacio de nombres del ámbito de la clase.
v.oddou