How are the __cplusplus directive defined in various compilers?

Learn how are the __cplusplus directive defined in various compilers? with practical examples, diagrams, and best practices. Covers c++, preprocessor-directive, compiler-version development techniq...

Understanding the __cplusplus Directive Across Compilers

Hero image for How are the __cplusplus directive defined in various compilers?

Explore how the __cplusplus preprocessor directive is defined by various C++ compilers, its historical evolution, and its utility in writing portable code.

The __cplusplus preprocessor directive is a fundamental feature in C++ development, providing a mechanism to identify the C++ standard version supported by the compiler. This allows developers to write conditional code that adapts to different language features and behaviors. However, the exact value of __cplusplus can vary significantly between compilers and their versions, leading to potential portability challenges. This article delves into the specifics of how major compilers define this directive and offers guidance on leveraging it effectively.

The Purpose and Evolution of __cplusplus

The __cplusplus macro is defined by the C++ standard to indicate that the compilation unit is being compiled as C++ code. Its value is an integer literal representing the year and month of the C++ standard version being used. For instance, C++98/03 is represented by 199711L, C++11 by 201103L, C++14 by 201402L, C++17 by 201703L, and C++20 by 202002L. Future standards will follow a similar pattern.

Historically, compilers were slow to update this macro, often defining it as 1 or 199711L even when supporting newer features. This made it difficult to reliably detect modern C++ standards. The situation has improved significantly with recent compiler versions, which now generally define __cplusplus correctly when the appropriate standard flag (e.g., -std=c++17, /std:c++latest) is used.

flowchart TD
    A["Start Compilation"] --> B{"Is `__cplusplus` defined?"}
    B -- Yes --> C{"Compiler supports C++ standard?"}
    C -- Yes --> D["Set `__cplusplus` to standard value"]
    C -- No --> E["Set `__cplusplus` to `199711L` (or `1`)"]
    B -- No --> F["Compile as C (or error)"]
    D --> G["Conditional Compilation"]
    E --> G
    F --> G
    G --> H["End Compilation"]
    subgraph Compiler Behavior
        D
        E
    end

General flow of __cplusplus definition during compilation

Compiler-Specific __cplusplus Definitions

While the standard dictates the values, real-world compiler implementations have nuances. It's crucial to understand how different compilers behave.

GCC (GNU Compiler Collection)

GCC generally adheres to the standard. When compiling with -std=c++XX (e.g., -std=c++17), __cplusplus will be set to the corresponding standard value. If no specific standard is requested, it often defaults to a value reflecting the latest supported standard or a specific older standard depending on the version.

Clang (LLVM Compiler Frontend)

Clang also follows the standard closely. Similar to GCC, using -std=c++XX will set __cplusplus appropriately. Clang is known for its good compliance with C++ standards.

MSVC (Microsoft Visual C++)

Microsoft Visual C++ historically defined __cplusplus as 199711L by default, regardless of the C++ standard version it actually supported. This was a significant source of frustration for developers. However, starting with Visual Studio 2017 version 15.7, MSVC introduced the /Zc:__cplusplus compiler option. When enabled, this option makes __cplusplus report the correct standard version. Without it, it defaults to 199711L for backward compatibility.

Other Compilers (Intel C++, Embarcadero C++ Builder, etc.)

Other compilers generally aim to follow the standard, but their default behaviors or specific flags might vary. Always consult the documentation for your specific compiler and version.

#include <iostream>

int main() {
    std::cout << "__cplusplus: " << __cplusplus << std::endl;

    #if __cplusplus >= 202002L
        std::cout << "C++20 or later detected." << std::endl;
    #elif __cplusplus >= 201703L
        std::cout << "C++17 or later detected." << std::endl;
    #elif __cplusplus >= 201402L
        std::cout << "C++14 or later detected." << std::endl;
    #elif __cplusplus >= 201103L
        std::cout << "C++11 or later detected." << std::endl;
    #else
        std::cout << "Pre-C++11 or unknown standard detected." << std::endl;
    #endif

    return 0;
}

Example C++ code to check the __cplusplus value and conditionally compile.

Writing Portable Code with __cplusplus

To write robust and portable C++ code that adapts to different compiler versions and standards, it's best practice to use __cplusplus for conditional compilation. However, be mindful of the historical quirks, especially with older MSVC versions.

For detecting specific features that might not directly map to a standard version (e.g., specific library features), it's often better to use feature test macros (like __has_include, __cpp_lib_filesystem, etc.) if available, as they provide more granular control and are less prone to compiler-specific __cplusplus issues. However, for broad standard version checks, __cplusplus remains the primary mechanism.

GCC/Clang

Compile for C++17

g++ -std=c++17 your_code.cpp -o your_program

Compile for C++20

clang++ -std=c++20 your_code.cpp -o your_program

MSVC

Compile for C++17 with correct __cplusplus

cl /std:c++17 /Zc:__cplusplus your_code.cpp

Compile for C++20 (latest) with correct __cplusplus

cl /std:c++latest /Zc:__cplusplus your_code.cpp