Namespaces
Variants

Conditional inclusion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Preprocessor
#if #ifdef #ifndef #else #elif #elifdef #elifndef #endif
(C++23) (C++23)
(C++26)

El preprocesador admite la compilación condicional de partes del archivo fuente. Este comportamiento se controla mediante las directivas #if , #else , #elif , #ifdef , #ifndef , #elifdef , #elifndef (desde C++23) , y #endif .

Contenidos

Sintaxis

#if expresión
#ifdef identificador
#ifndef identificador
#elif expresión
#elifdef identificador (desde C++23)
#elifndef identificador (desde C++23)
#else
#endif

Explicación

El bloque de preprocesamiento condicional comienza con #if , #ifdef o #ifndef , luego opcionalmente incluye cualquier número de directivas #elif , #elifdef , o #elifndef (desde C++23) , luego opcionalmente incluye como máximo una directiva #else y finaliza con la directiva #endif . Cualquier bloque de preprocesamiento condicional interno se procesa por separado.

Cada una de las directivas #if , #ifdef , #ifndef , #elif , #elifdef , #elifndef (desde C++23) , y #else controla el bloque de código hasta la primera directiva #elif , #elifdef , #elifndef (desde C++23) , #else , #endif que no pertenezca a ningún bloque de preprocesamiento condicional interno.

#if , #ifdef y #ifndef evalúan la condición especificada (ver abajo) y si resulta verdadera, compilan el bloque de código controlado. En ese caso, las directivas posteriores #else , #elifdef , #elifndef , (desde C++23) y #elif se ignoran. De lo contrario, si la condición especificada evalúa como falsa, se omite el bloque de código controlado y se procesa la directiva posterior #else , #elifdef , #elifndef , (desde C++23) o #elif (si existe). Si la directiva posterior es #else , el bloque de código controlado por la directiva #else se compila incondicionalmente. De lo contrario, la directiva #elif , #elifdef , o #elifndef (desde C++23) actúa como si fuera una directiva #if : verifica la condición, compila u omite el bloque de código controlado según el resultado, y en este último caso procesa las directivas posteriores #elif , #elifdef , #elifndef , (desde C++23) y #else . El bloque de preprocesamiento condicional finaliza con la directiva #endif .

Evaluación de condiciones

#if, #elif

expresión puede contener operadores unarios en forma de defined identificador o defined ( identificador ) . El resultado es 1 si el identificador fue definido como nombre de macro , de lo contrario el resultado es 0 .

expression también puede contener las siguientes expresiones:

  • __has_include expressions, que detecta si existe un archivo de cabecera o fuente.
  • __has_cpp_attribute expressions, que detecta si un token de atributo dado es compatible y su versión soportada.
(since C++20)
  • __has_embed expressions, que detecta si un recurso está disponible para ser incrustado.
(since C++26)

Los identificadores mencionados anteriormente se tratan como si fueran los nombres de macros definidas en este contexto.

(since C++17)

Después de toda la expansión de macros y evaluación de defined y las expresiones descritas anteriormente, cualquier identificador que no sea un literal booleano es reemplazado con el número 0 (esto incluye identificadores que son léxicamente palabras clave, pero no tokens alternativos como and ).

Entonces la expresión se evalúa como una integral constant expression .

Si la expresión evalúa a un valor distinto de cero, el bloque de código controlado se incluye y se omite en caso contrario.

Nota: Hasta la resolución de CWG issue 1955 , #if cond1 ... #elif cond2 es diferente de #if cond1 ... #else seguido por #if cond2 porque si cond1 es verdadero, el segundo #if se omite y cond2 no necesita estar bien formado, mientras que #elif 's cond2 debe ser una expresión válida. A partir de CWG 1955, #elif que conduce al bloque de código omitido también se omite.

Directivas combinadas

Comprueba si el identificador fue definido como un nombre de macro .

#ifdef identifier es esencialmente equivalente a #if defined identifier .

#ifndef identifier es esencialmente equivalente a #if !defined identifier .

#elifdef identifier es esencialmente equivalente a #elif defined identifier .

#elifndef identifier es esencialmente equivalente a #elif !defined identifier .

(desde C++23)

Notas

Mientras que las directivas #elifdef y #elifndef están destinadas para C++23, se recomienda a las implementaciones que las retroporten a los modos de lenguaje anteriores como extensiones conformes.

Ejemplo

#define ABCD 2
#include <iostream>
int main()
{
#ifdef ABCD
    std::cout << "1: yes\n";
#else
    std::cout << "1: no\n";
#endif
#ifndef ABCD
    std::cout << "2: no1\n";
#elif ABCD == 2
    std::cout << "2: yes\n";
#else
    std::cout << "2: no2\n";
#endif
#if !defined(DCBA) && (ABCD < 2*4-3)
    std::cout << "3: yes\n";
#endif
// Nota: si un compilador no soporta las directivas #elifdef/#elifndef de C++23
// entonces se seleccionará el bloque "inesperado" (ver abajo).
#ifdef CPU
    std::cout << "4: no1\n";
#elifdef GPU
    std::cout << "4: no2\n";
#elifndef RAM
    std::cout << "4: yes\n"; // bloque esperado
#else
    std::cout << "4: no!\n"; // selecciona inesperadamente este bloque saltando
                             // directivas desconocidas y "saltando" directamente
                             // desde "#ifdef CPU" a este bloque "#else"
#endif
// Para solucionar el problema anterior podemos definir condicionalmente la
// macro ELIFDEF_SUPPORTED solo si se soportan las directivas #elifdef/#elifndef de C++23.
#if 0
#elifndef UNDEFINED_MACRO
#define ELIFDEF_SUPPORTED
#else
#endif
#ifdef ELIFDEF_SUPPORTED
    #ifdef CPU
        std::cout << "4: no1\n";
    #elifdef GPU
        std::cout << "4: no2\n";
    #elifndef RAM
        std::cout << "4: yes\n"; // bloque esperado
    #else
        std::cout << "4: no3\n";
    #endif
#else // cuando #elifdef no está soportado, usar el antiguo y verboso "#elif defined"
    #ifdef CPU
        std::cout << "4: no1\n";
    #elif defined GPU
        std::cout << "4: no2\n";
    #elif !defined RAM
        std::cout << "4: yes\n"; // bloque esperado
    #else
        std::cout << "4: no3\n";
    #endif
#endif
}

Salida posible:

1: yes
2: yes
3: yes
4: no!
4: yes

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 1955 C++98 se requería que la expresión de #elif fuera válida se omite el #elif fallido

Véase también

Documentación de C para Inclusión condicional