Unique address for constexpr variable

Is it possible to have a unique address allocated for the constexpr variable, that is, for all translation units where this variable is available (usually through the 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; } 

a.cc and b.cc separately and linking them together using gcc 4.7, I see two different addresses. If I add the extern keyword to the header, I get a duplicate symbol _foo in: ao and bo linker error, which I find rather unexpected, because I thought adding extern would rather cause the compiler to import this symbol from another object instead exporting it from the current object. But it seems that my understanding of how everything works was wrong.

Is there any reasonable way to have constexpr declared in one heading so that all translation units can use it in their constant expressions and that all translation units agree on the address of this symbol? I expect some additional code to denote a single translation unit, where this character really belongs, as are the variables extern and not extern without constexpr .

+10
c ++ c ++ 11 memory-address constexpr extern
source share
3 answers

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

foo.hpp:

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

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 
+12
source share

I think constexpr is more for functions whose return value is constant. You can bind a constant variable to the return value of the constexpr function and set it instead. For example:

 // constexpr.h #ifndef __CONSTEXPR_H #define __CONSTEXPR_H extern const int foo; #endif // __CONSTEXPR_H // constexpr.cpp #include "constexpr.h" constexpr int foo_expr() { return 42; } const int foo = foo_expr(); // unit1.cpp #include <iostream> #include "constexpr.h" void unit1_print_foo() { std::cout << &foo << " = " << foo << std::endl; } // unit2.cpp #include <iostream> #include "constexpr.h" void unit2_print_foo() { std::cout << &foo << " = " << foo << std::endl; } // main.cpp extern void unit1_print_foo(); extern void unit2_print_foo(); int main(int, char**) { unit1_print_foo(); unit2_print_foo(); } 

My result:

 $ g++-4.7 -std=c++11 constexpr.cpp unit1.cpp unit2.cpp main.cpp -o test && ./test 0x400ae4 = 42 0x400ae4 = 42 

However, usually it should be enough for the foo_expr function foo_expr be externally visible, and the callers to use foo_expr() to get the value instead of treating it as a variable.

+1
source share

C ++ 17 inline variables

This amazing C ++ 17 feature allows us to:

  • it is convenient to use only one memory address for each constant
  • save it as constexpr : How to declare constexpr extern?
  • do it in one 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:

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 built-in variables work?

C ++ standard for built-in variables

The C ++ standard ensures that addresses are the same. C ++ 17 Standard project N4659 10.1.6 " Built-in specifier":

6 A built-in function or a variable with external communication must have the same address in all translation units.

cppreference https://en.cppreference.com/w/cpp/language/inline explains that if static not set, then it has an external connection.

Implementing Inline Variables

We can observe how this 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 talks about u :

The "U" Symbol is a unique global symbol. This is a GNU extension for the standard ELF character binding set. For such a symbol, the dynamic linker will ensure that there is only one symbol with this name and type throughout the process.

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

+1
source share

All Articles