How to declare constexpr extern?

前端 未结 6 914
耶瑟儿~
耶瑟儿~ 2020-11-29 07:43

Is it possible to declare a variable extern constexpr and define it in another file?

I tried it but the compiler gives error:

De

相关标签:
6条回答
  • 2020-11-29 08:20

    What you probably want is extern and constexpr initialization, e.g.:

    // in header
    extern const int g_n;
    
    // in cpp
    constexpr int g_n = 2;
    

    This is support though in Visual Studio 2017 only through conformance mode:

    • /Zc:externConstexpr (Enable extern constexpr variables)
    • constexpr definition of extern const variable
    0 讨论(0)
  • 2020-11-29 08:21

    no you can't do it, here's what the standard says (section 7.1.5):

    1 The constexpr specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). If any declaration of a function, function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. Function parameters cannot be declared constexpr. — end note ]

    some examples given by the standard:

      constexpr void square(int &x);  // OK: declaration
      constexpr int bufsz = 1024;  // OK: definition
      constexpr struct pixel {  // error: pixel is a type
        int x;
        int y;
        constexpr pixel(int);  // OK: declaration
      };
    
      extern constexpr int memsz; // error: not a definition
    
    0 讨论(0)
  • 2020-11-29 08:21

    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
    • do it in a single line from one header

    main.cpp

    #include <cassert>
    
    #include "notmain.hpp"
    
    int main() {
        // Both files see the same memory address.
        assert(&notmain_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 &notmain_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.

    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.

    See also: How do inline variables work?

    Tested in GCC 7.4.0, Ubuntu 18.04.

    0 讨论(0)
  • 2020-11-29 08:26

    No. Extern constexpr does not make any sense. Please read http://en.cppreference.com/w/cpp/language/constexpr

    i.e. the bit " it must be immediately constructed or assigned a value. "

    0 讨论(0)
  • 2020-11-29 08:28

    I agree with 'swang' above, but there is a consequence. Consider:

    ExternHeader.hpp

    extern int e; // Must be extern and defined in .cpp otherwise it is a duplicate symbol.
    

    ExternHeader.cpp

    #include "ExternHeader.hpp"
    int e = 0;
    

    ConstexprHeader.hpp

    int constexpr c = 0; // Must be defined in header since constexpr must be initialized.
    

    Include1.hpp

    void print1();
    

    Include1.cpp

    #include "Include1.hpp"
    #include "ExternHeader.hpp"
    #include "ConstexprHeader.hpp"
    #include <iostream>
    
    void print1() {
        std::cout << "1: extern = " << &e << ", constexpr = " << &c << "\n";
    }
    

    Include2.hpp

    void print2();
    

    Include2.cpp

    #include "Include2.hpp"
    #include "ExternHeader.hpp"
    #include "ConstexprHeader.hpp"
    #include <iostream>
    
    void print2() {
        std::cout << "2: extern = " << &e << ", constexpr = " << &c << "\n";
    }
    

    main.cpp

    #include <iostream>
    #include "Include1.hpp"
    #include "Include2.hpp"
    
    int main(int argc, const char * argv[]) {
        print1();
        print2();
        return 0;
    }
    

    Which prints:

    1: extern = 0x1000020a8, constexpr = 0x100001ed0
    2: extern = 0x1000020a8, constexpr = 0x100001ed4
    

    IE the constexpr is allocated twice whereas the extern is allocated once. This is counterintuitive to me, since I 'expect' constexpr to be more optimized than extern.

    Edit: const and constexpr have the same behaviour, with regard to allocation, therefore from that point of view the behaviour is as expected. Though, as I said, I was surprised when I came across the behaviour of constexpr.

    0 讨论(0)
  • 2020-11-29 08:39

    Yes it somewhat is...

    //===================================================================
    // afile.h
    
    #ifndef AFILE
    #define AFILE
    
    #include <cstddef>
    #include <iostream>
    
    enum class IDs {
    
      id1,
      id2,
      id3,
      END
    
    };
    
    // This is the extern declaration of a **constexpr**, use simply **const**
    extern const int ids[std::size_t(IDs::END)];
    
    // These functions will demonstrate its usage
    
    template<int id> void Foo() { std::cout << "I am " << id << std::endl; }
    
    extern void Bar();
    
    #endif // AFILE
    
    //===================================================================
    // afile.cpp
    
    #include "afile.h"
    
    // Here we define the consexpr. 
    // It is **constexpr** in this unit and **const** in all other units
    constexpr int ids[std::size_t(IDs::END)] = {
    
      int(IDs::id1),
      int(IDs::id2),
      int(IDs::id3)
    
    };
    
    // The Bar function demonstrates that ids is really constexpr
    void Bar() {
    
      Foo<ids[0]      >();
      Foo<ids[1] + 123>();
      Foo<ids[2] / 2  >();
    
    }
    
    //===================================================================
    // bfile.h
    
    #ifndef BFILE
    #define BFILE
    
    // These functions will demonstrate usage of constexpr ids in an extern unit
    
    extern void Baz();
    extern void Qux();
    
    
    #endif // BFILE
    
    //===================================================================
    // bfile.cpp
    
    #include "afile.h"
    
    // Baz demonstrates that ids is (or works as) an extern field
    void Baz() {
    
      for (int i: ids) std::cout << i << ", ";
      std::cout << std::endl;
    
    }
    
    // Qux demonstrates that extern ids cannot work as constexpr, though
    void Qux() {
    
    #if 0 // changing me to non-0 gives you a compile-time error...
    
      Foo<ids[0]>();
    
    #endif
    
      std::cout << "Qux: 'I don't see ids as consexpr, indeed.'" 
                << std::endl;
    
    }
    
    //===================================================================
    // main.cpp
    
    #include "afile.h"
    #include "bfile.h"
    
    int main(int , char **)
    {
    
      Bar();
      Baz();
      Qux();
    
      return 0;
    }
    
    0 讨论(0)
提交回复
热议问题