c++ - Una función constexpr con inicialización retardada de variables locales.
(3)
Estoy tratando de escribir una función constexpr
de la forma:
constexpr int foo(bool cond) {
int a, b, c;
if (cond) {
a = 1;
b = 2;
c = 3;
}
else {
a = -1;
b = -2;
c = -3;
}
return a + b + c;
}
Sin embargo, el compilador se queja de que estoy usando variables sin inicializar, a pesar del hecho de que se garantiza la eventual inicialización de las variables locales.
Podría reescribir la función para usar operadores ternarios, es decir, int a = cond ? 1 : -1;
int a = cond ? 1 : -1;
, etc., pero preferiría no hacerlo. ¿Hay alguna manera de convencer al compilador de que las variables locales se inicializarán?
Sin embargo, el compilador se queja de que estoy usando una variable no inicializada, a pesar del hecho de que la inicialización de las variables locales está garantizada.
El estándar requiere que todas las variables locales en una función constexpr
estén inicializadas.
De §7.1.5 , par. 3 ( [dcl.constexpr] ) :
La definición de una función
constexpr
deberá cumplir los siguientes requisitos: [...]su función-body será
= delete
,= default
, o una instrucción compuesta que no contenga [...]una definición de una variable de tipo no literal o de duración de almacenamiento de subprocesos o estática o para la que no se realiza ninguna inicialización [...]
constexpr int uninit() { int a; // error: variable is uninitialized return a; }
En C ++ 17, puede usar std::tuple
, enlaces estructurados y IIFE (expresión de función invocada de inmediato) para conservar su estructura original:
constexpr int foo(bool cond)
{
const auto [a, b, c] = [&cond]
{
if (cond)
{
return std::tuple(1, 2, 3);
}
else
{
return std::tuple(-1, -2, -3);
}
}();
return a + b + c;
}
Dado que su condición y ramificación es trivial, un operador ternario será suficiente. El fragmento de código anterior puede ayudarlo si en el futuro su lógica de inicialización se vuelve más compleja, pero la siguiente debe ser suficiente:
constexpr int foo(bool cond)
{
const auto [a, b, c] = cond ? std::tuple(1, 2, 3)
: std::tuple(-1, -2, -3);
return a + b + c;
}
En C ++ 14, puedes usar std::make_tuple
y std::get
lugar:
constexpr int foo(bool cond)
{
const auto abc = cond ? std::make_tuple(1, 2, 3)
: std::make_tuple(-1, -2, -3);
return std::get<0>(abc) + std::get<1>(abc) + std::get<2>(abc);
}
En C ++ 11 puedes dividir la función en dos más pequeñas:
template <typename TTuple>
constexpr int sum3(const TTuple& abc)
{
return std::get<0>(abc) + std::get<1>(abc) + std::get<2>(abc);
}
constexpr int foo(bool cond)
{
return cond ? sum3(std::make_tuple(1, 2, 3))
: sum3(std::make_tuple(-1, -2, -3));
}
Sin embargo, la solución de Barry es definitivamente mejor si decides seguir ese camino.
Todas las soluciones anteriores:
Haga que las variables a,
b
,c
const
, lo que siempre es bueno.Solo realice una comprobación en
cond
, para parecerse mucho a la estructura del código en el OP.
Sin embargo, el compilador se queja de que estoy usando una variable no inicializada, a pesar del hecho de que la inicialización de las variables locales está garantizada.
Inicializar, o inicializar no, no hay "inicialización eventual". Y, para constexpr
funciones constexpr
, hay un requisito que, en [dcl.constexpr]:
La definición de una función
constexpr
deberá cumplir los siguientes requisitos: [...] su función-body será= delete
,= default
, o una declaración compuesta que no contiene una definición de una variable de non -tipo literal o de duración de almacenamiento de subprocesos o estática o para el que no se realiza ninguna inicialización
No se pueden tener variables sin inicializar en constexpr
funciones constexpr
, que es lo que a
, b
y c
son para usted.
¿Entonces que puedes hacer? Podría simplemente inicializar a cero, b
y c
. Eso evita este requisito. O puede inicializar a
, b
y c
dentro de cada ámbito en el if
. O podría diferir a otra función constexpr
para hacer la suma:
constexpr int f(int a, int b, int c) { return a+b+c; };
constexpr int foo(bool cond) {
if (cond) {
return f(1,2,3);
}
else {
return f(-1,-2,-3);
}
}
Hay muchas maneras de evitar esto.
@Borgleader es suficiente:
constexpr int foo(bool cond) {
int a=0, b=0, c=0;
if (cond) {
a = 1;
b = 2;
c = 3;
}
else {
a = -1;
b = -2;
c = -3;
}
return a + b + c;
}
compila sin errores en C ++ 11 y solo advierte que la declaración de variables en una función constexpr es una extensión de C ++ 14 y sin advertencia en el modo C ++ 14 (con CLang 3.4.1)
Esto es limpio, fácil de leer y escribir, y está cerca del código original. Pero indudablemente, la solución de @Barry es mejor.