💡 Resumen:La familia const de C++ al completo: const vs constexpr vs constinit, consteval y miembros const, y cómo el deducing this de C++23 colapsa cuatro sobrecargas const/ref en una sola función.
Última actualización:2026-10-24
La familia completa de const en C++¶
Esquema
- const
- constexpr
- C++20
- consteval
- constinit
Nota: todos los ejemplos de código de este artículo se han publicado en el planeta de conocimiento.
1. const¶
const es una de las primeras palabras clave que encontramos al aprender C++/C; suele utilizarse como cv-qualifier para indicar que el objeto es inmutable. No garantiza la evaluación en tiempo de compilación: el momento concreto de la evaluación lo decide el compilador. Por ejemplo:
const double count_1 = 3.3;
std::array<double, static_cast<int>(count_1)> moreDoubles1 {1.1, 2.2, 3.3};
Pero escrito así funciona con normalidad:
La clave está en la evaluación de expresiones en tiempo de compilación, y por eso C++11 introdujo constexpr.2. constexpr¶
constexpr permite especificar que el valor de una variable puede aparecer en una expresión constante; basta con cambiar el const del ejemplo anterior por constexpr para que funcione con normalidad.
constexpr double count_2 = 3.3;
std::array<double, static_cast<int>(count_2)> moreDoubles2 {1.1, 2.2, 3.3};
constexpr no tiene por qué ejecutarse únicamente en tiempo de compilación. Su significado esencial es que puede calcularse en tiempo de compilación, pero si se usa una variable o función constexpr en tiempo de ejecución, también puede ejecutarse en tiempo de ejecución.
Por ejemplo: result1 y result2 son completamente distintos; result1 se calcula en tiempo de compilación, pero result2 se calcula en tiempo de ejecución. Una pregunta al aire: ¿sabes por qué?
Así pues, no porque una función esté marcada con constexpr se calculará necesariamente en tiempo de compilación.
constexpr int compileTimeSum(int a, int b) {
return a + b;
}
int main() {
constexpr int result1 = compileTimeSum(3, 5);
int x = 10;
int result2 = compileTimeSum(x, 20);
return 0;
}
3. C++20¶
3.1 consteval¶
consteval resuelve el problema anterior: solo puede aplicarse a funciones y fuerza que todas las llamadas se realicen en tiempo de compilación. Si ahora cambiamos el constexpr del ejemplo anterior por consteval, ¿qué le ocurre a result2?
Pues se produce un error: call to consteval function 'compileTimeSum(x, 20)' is not a constant expression
consteval int compileTimeSum(int a, int b) {
return a + b;
}
int main() {
constexpr int result1 = compileTimeSum(3, 5);
int x = 10;
int result2 = compileTimeSum(x, 20);
return 0;
}
3.2 constinit¶
constinit permite forzar la inicialización constante de variables estáticas o locales a un hilo.
- No puede usarse junto con constexpr ni consteval
- No es un especificador de tipo; su función es assert
- Garantiza que la variable se inicialice estáticamente
- Solo puede aplicarse a static o thread_local
Por ejemplo: lo siguiente da un error de compilación: error: 'constinit' can only be applied to a variable with static or thread storage duration
constinit int a = 100;
int main() {
++a;
constinit auto b = 100; // error
// b has thread storage duration
constinit thread_local auto b = 100;
}
C++23 deducing this: escribe las sobrecargas const/ref una sola vez¶
Quien avanza por la familia const acaba chocando con el mismo muro: un getter que deba comportarse bien con objetos const y no const, lvalues y rvalues, necesita hasta cuatro sobrecargas casi idénticas — y el día que alguien edite solo una, empiezan a divergir en silencio.
El «deducing this» de C++23 (parámetro de objeto explícito, propuesta P0847) elimina la duplicación. La función miembro declara su propio objeto como parámetro explícito; el compilador deduce su tipo junto con la constancia y la categoría de valor, y una sola plantilla cubre los cuatro casos:
class Widget {
int value_{42};
public:
// Before C++23 you needed up to four overloads:
// int& get() & { return value_; }
// int get() const& { return value_; }
// int&& get() && { return std::move(value_); }
// ...
// C++23 deducing this — one template covers them all:
template <typename Self>
auto&& get(this Self&& self) {
return std::forward<Self>(self).value_;
}
};
Al llamar get() sobre un lvalue const, Self se deduce como const Widget&; sobre un rvalue, como Widget&& y std::forward mueve. Decidir qué debe ser const sigue siendo trabajo de lo explicado antes; deducing this solo quita el copia-y-pega.
La característica también asume dos tareas que hacía el CRTP — mezclar comportamiento y deducir el tipo derivado — sin el boilerplate de la plantilla base. Soporte: GCC 14+, Clang 18+, MSVC desde VS 2022 17.2.
Preguntas frecuentes¶
¿Puede deducing this sustituir las sobrecargas const?¶
Sí en el caso común. Una única plantilla sobre Self&& con std::forward se comporta bien con &, const&, && y const&&. Solo escribes sobrecargas separadas cuando el comportamiento difiere de verdad por calificación, que es raro.
¿Qué compiladores soportan deducing this?¶
GCC 14 o superior, Clang 18 o superior y MSVC desde Visual Studio 2022 17.2. Es una característica de C++23: compila con -std=c++23 (o c++2b en drivers antiguos).
¿Es deducing this lo mismo que CRTP?¶
Se solapan: ambos deducen el tipo más derivado. Deducing this no necesita plantilla base, funciona en lambdas (¡lambdas recursivas!) y mantiene la jerarquía limpia; el CRTP sigue sirviendo cuando el mixin debe ser una clase base reutilizable.
Temas relacionados¶
- Funciones virtuales puras y clases abstractas en C++ | C++那些事
- Aserciones assert en C++ | C++那些事
- Campos de bits en C++ | C++那些事
- Implementar el polimorfismo de C++ en lenguaje C | C++那些事
- const en C++ explicado: constantes y puntero const vs puntero a const | C++那些事
- El operador de desreferencia * en C++ | C++那些事
- Inicialización y asignación en C++ | C++那些事
- Formas de devolver varios valores en C++ | C++那些事
- La palabra clave mutable en C++ | C++那些事
- C++ restrict y optimización del compilador | C++那些事