Define constant variables in C++ header

前端 未结 6 1373
既然无缘
既然无缘 2020-11-30 18:30

A program I am working on has many constants that apply throughout all classes. I want to make one header file \"Constants.h\", and be able to declare all the relevant const

6条回答
  •  半阙折子戏
    2020-11-30 18:59

    C++17 inline variables

    This awesome C++17 feature allow us to:

    • conveniently use just a single memory address for each constant
    • store it as a constexpr: How to declare constexpr extern?
    • do it in a single line from one header

    main.cpp

    #include 
    
    #include "notmain.hpp"
    
    int main() {
        // Both files see the same memory address.
        assert(¬main_i == notmain_func());
        assert(notmain_i == 42);
    }
    

    notmain.hpp

    #ifndef NOTMAIN_HPP
    #define NOTMAIN_HPP
    
    inline constexpr int notmain_i = 42;
    
    const int* notmain_func();
    
    #endif
    

    notmain.cpp

    #include "notmain.hpp"
    
    const int* notmain_func() {
        return ¬main_i;
    }
    

    Compile and run:

    g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
    g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
    g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
    ./main
    

    GitHub upstream.

    See also: How do inline variables work?

    C++ standard on inline variables

    The C++ standard guarantees that the addresses will be the same. C++17 N4659 standard draft 10.1.6 "The inline specifier":

    6 An inline function or variable with external linkage shall have the same address in all translation units.

    cppreference https://en.cppreference.com/w/cpp/language/inline explains that if static is not given, then it has external linkage.

    Inline variable implementation

    We can observe how it is implemented with:

    nm main.o notmain.o
    

    which contains:

    main.o:
                     U _GLOBAL_OFFSET_TABLE_
                     U _Z12notmain_funcv
    0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                     U __assert_fail
    0000000000000000 T main
    0000000000000000 u notmain_i
    
    notmain.o:
    0000000000000000 T _Z12notmain_funcv
    0000000000000000 u notmain_i
    

    and man nm says about u:

    "u" The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use.

    so we see that there is a dedicated ELF extension for this.

    C++17 standard draft on "global" const implies static

    This is the quote for what was mentioned at: https://stackoverflow.com/a/12043198/895245

    C++17 n4659 standard draft 6.5 "Program and linkage":

    3 A name having namespace scope (6.3.6) has internal linkage if it is the name of

    • (3.1) — a variable, function or function template that is explicitly declared static; or,
    • (3.2) — a non-inline variable of non-volatile const-qualified type that is neither explicitly declared extern nor previously declared to have external linkage; or
    • (3.3) — a data member of an anonymous union.

    "namespace" scope is what we colloquially often refer to as "global".

    Annex C (informative) Compatibility, C.1.2 Clause 6: "basic concepts" gives the rationale why this was changed from C:

    6.5 [also 10.1.7]

    Change: A name of file scope that is explicitly declared const, and not explicitly declared extern, has internal linkage, while in C it would have external linkage.

    Rationale: Because const objects may be used as values during translation in C++, this feature urges programmers to provide an explicit initializer for each const object. This feature allows the user to put const objects in source files that are included in more than one translation unit.

    Effect on original feature: Change to semantics of well-defined feature.

    Difficulty of converting: Semantic transformation.

    How widely used: Seldom.

    See also: Why does const imply internal linkage in C++, when it doesn't in C?

    Tested in GCC 7.4.0, Ubuntu 18.04.

提交回复
热议问题