c++ - ¿Cómo puede ser constexpr std:: chrono:: duration:: duration()?
c++11 (1)
7.1.5 El especificador constexpr
[dcl.constexpr] dice:
La definición de un constructor
constexpr
deberá cumplir los siguientes requisitos:
- la clase no tendrá ninguna clase base virtual;
- para un constructor predeterminado de copiar / mover, la clase no tendrá un subobjeto mutable que sea un miembro variante;
- cada uno de los tipos de parámetros será un tipo literal;
- su función-body no será una función-try-block;
Además, su función-body será = delete, o cumplirá los siguientes requisitos:
- o bien su función-cuerpo será = por defecto, o la declaración compuesta de su función-cuerpo deberá satisfacer los requisitos para una función-cuerpo de una función constexpr;
- todo miembro no variante de datos no estáticos y subobjetos de clase base se inicializarán (12.6.2);
- si la clase es una unión que tiene miembros variantes (9.5), se debe inicializar exactamente uno de ellos;
- si la clase es una clase similar a una unión, pero no es una unión, para cada uno de sus miembros anónimos de la unión que tienen miembros variantes, se debe inicializar exactamente uno de ellos;
- para un constructor no delegante, cada constructor seleccionado para inicializar miembros de datos no estáticos y subobjetos de clase base será un constructor constexpr;
- para un constructor delegante, el constructor de destino será un constructor constexpr.
En pocas palabras, = default
es una definición válida de un constructor por defecto constexpr
siempre que se cumplan los otros requisitos anteriores.
Entonces, ¿cómo funciona esto con construcciones no inicializadas ?
No lo hace
Por ejemplo:
constexpr seconds x1{};
Lo anterior funciona e inicializa x1
a 0s
. Sin embargo:
constexpr seconds x2;
error: default initialization of an object of const type ''const seconds''
(aka ''const duration<long long>'') without a user-provided default
constructor
constexpr seconds x2;
^
{}
1 error generated.
Por lo tanto, para crear una duration
construida predeterminada de constexpr
, debe inicializarla con cero . Y la implementación = default
permite inicializar a cero con {}
.
Demo completa de trabajo:
template <class Rep>
class my_duration
{
Rep rep_;
public:
constexpr my_duration() = default;
};
int
main()
{
constexpr my_duration<int> x{};
}
Barra lateral interesante
Aprendí algo al escribir esta respuesta, y quería compartir:
Me seguí preguntando por qué lo siguiente no funciona:
using Rep = int;
class my_duration
{
Rep rep_;
public:
constexpr my_duration() = default;
};
int
main()
{
constexpr my_duration x{};
}
error: defaulted definition of default constructor is not constexpr
constexpr my_duration() = default;
^
¿Por qué hacer que esta clase sea una no-plantilla rompe el constructor predeterminado constexpr
?
Entonces intenté esto:
using Rep = int;
class my_duration
{
Rep rep_;
public:
my_duration() = default; // removed constexpr
};
int
main()
{
constexpr my_duration x{};
}
Y a los compiladores les gusta otra vez.
Si todavía no hay un problema de CWG sobre esto, probablemente debería haberlo. El comportamiento parece un poco inconsistente. Y esto es probablemente porque nosotros (la industria entera) todavía estamos aprendiendo sobre constexpr
.
El constructor predeterminado de std::chrono::duration
se define de la siguiente manera:
constexpr duration() = default;
(Por ejemplo, vea cppreference.com o la fuente libstdc ++).
Sin embargo, cppreference.com también dice esto acerca de los constructores constexpr
:
Un constructor constexpr debe cumplir los siguientes requisitos:
...
cada clase base y cada miembro no estático deben inicializarse , ya sea en la lista de inicialización de los constructores o por un miembro con inicializador de refuerzo o igual. Además, cada constructor involucrado debe ser un constructor constexpr y cada cláusula de cada inicializador de refuerzo o igual debe ser una expresión constante
Y en caso de que estuviera confundido acerca de los constructores predeterminados, cppreference.com parece decir que los constructores predeterminados creados con = default
no se definen de manera diferente a los constructores predeterminados implícitos.
Sin embargo, el tipo de rep
para (la mayoría) de las duraciones es un tipo entero desnudo. Entonces, ¿no debería el constructor por defecto explícito = default
para la duration
ser equivalente a
constexpr duration() {}
que, por supuesto, dejaría la variable miembro entero del tipo duration::rep
uninitialized? Y, de hecho, ¿no es el comportamiento estándar de la duration
tal que los valores construidos por defecto no están inicializados? (Pero no puedo encontrar una referencia que explícitamente diga esto).
Entonces, ¿cómo puede el constructor = default
para la duration
ser constexpr
si deja una variable miembro no estática sin inicializar? ¿Qué me estoy perdiendo?