Search code examples
c++c++11externmemory-addressconstexpr

Unique address for constexpr variable


Is it possible to have a unique address allocated for a constexpr variable, i.e. the same for all translation units where the variable is available (usually through a header)? Consider the following example:

// foo.hh
#include <iostream>
constexpr int foo = 42;

// a.cc
#include "foo.hh"
void a(void) { std::cout << "a: " << &foo << std::endl; }

// b.cc
#include "foo.hh"
extern void a(void);
int main(int argc, char** argv) {
  a();
  std::cout << "b: " << &foo << std::endl;
}

Compiling a.cc and b.cc separately, and linking them together using gcc 4.7, I see two different addresses printed. If I add the keyword extern in the header, I get a linker error duplicate symbol _foo in: a.o and b.o which I find kind of surprising, because I thought that adding extern would more likely cause the compiler to import that symbol from another object instead of exporting it from the current object. But it seems my understanding of how things work was wrong here.

Is there a reasonable way to have a constexpr declared in one header, such that all translation units can use it in their constant expressions, and such that all translation units agree as to the address of that symbol? I would expect some additional code to denote the single translation unit where this symbol actually belongs to, just like with extern and non-extern variables without constexpr.


Solution

  • If you need to take the address of constexpr variable, declare it as a static member variable. It can be used as a constant expression this way (as opposed to using a function returning a const).

    foo.h:

    #ifndef FOO_H
    #define FOO_H
    
    struct Foo {
      static constexpr int foo { 42 }; // declaration
    };
    
    #endif // FOO_H
    

    foo.cpp:

    #include "foo.hpp"
    
    constexpr int Foo::foo; // definition
    

    bar.cpp:

    #include "foo.hpp"
    
    const int* foo_addr() {
      return &Foo::foo;
    }
    
    int foo_val() {
      return Foo::foo;
    }
    

    main.cpp:

    #include <iostream>
    #include "foo.hpp"
    
    extern const int* foo_addr();
    extern int foo_val();
    
    constexpr int arr[Foo::foo] {}; // foo used as constant expression
    
    int main() {
      std::cout << foo_addr() << " = " << foo_val() << std::endl;
      std::cout << &Foo::foo << " = " << Foo::foo << std::endl;
    }
    

    Output:

    $ g++ -std=c++11 foo.cpp bar.cpp main.cpp -o test && ./test
    0x400a44 = 42
    0x400a44 = 42