Saltar a contenido

💡 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 int a = 1; // 编译时
const auto x = getdata(); // 运行时
A veces const puede usarse en expresiones constantes de tiempo de compilación, pero hay algunas excepciones, por ejemplo:

const double count_1 = 3.3;
std::array<double, static_cast<int>(count_1)> moreDoubles1 {1.1, 2.2, 3.3};
En este caso se produce un error: the value of 'count_1' is not usable in a constant expression

Pero escrito así funciona con normalidad:

const int count = 3;
std::array<double, count> doubles {1.1, 2.2, 3.3};
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;
}
¿Y quién puede garantizarlo entonces? Por eso llegó C++20.

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;
}
Si se usa consteval sobre una variable, también se produce un error: error: a variable cannot be declared 'consteval'.

https://en.cppreference.com/w/cpp/language/consteval

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

Comentarios