14

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.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
MvG
  • 57,380
  • 22
  • 148
  • 276
  • 3
    I believe your `foo` has internal linkage, so you're seeing two separate copies. The usual solution for your problem is to have an `extern const int foo` declared in the header and implemented as `const int foo = 42;` in *one* translation unit. But hten it can obviously not be a constant expression, since `int a[foo]` needs to be resolvable at compile time, and not just at link time. – Kerrek SB Feb 14 '13 at 10:13
  • 2
    Perhaps there's another way to achieve what you're trying to do. So... what exactly *are* you trying to do with this address? – Nicol Bolas Feb 14 '13 at 10:49
  • @NicolBolas: So far I'm trying to come to grips with `constexpr` in general. I was in the habit of using external linkage for `const` global variables to avoid duplicate memory allocation even if someone decided to take an address of such a beast. Now with `constexpr` this doesn't seem possible any more. So what I'm actually trying to find out is if there is some way to avoid data duplication even if some strange code I can't imagine right now decides to take addresses of these things all over the place. – MvG Feb 14 '13 at 11:06
  • 1
    It's not usual to speak of 'memory allocation' in this context, the same way it's not usually said that `4 + 4` involves memory allocation at all. – Luc Danton Feb 14 '13 at 11:51

3 Answers3

15

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
Pharap
  • 3,826
  • 5
  • 37
  • 51
mbeoayt
  • 166
  • 1
  • 3
9

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 <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 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.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • cppreference also explains that "Static data members of a class in namespace scope have external linkage if the class itself has external linkage (i.e. is not a member of unnamed namespace)." So as long as some class `Foo` is not a member of an unnamed namespace, then the `inline` static member `Foo::bar` will have external linkage, and the above applies. – monkey0506 Aug 15 '19 at 02:46
1

I think constexpr is meant more for functions whose return value is constant. You can bind a constant variable to the return value of a constexpr function and expose that externally 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 is:

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

However, it should usually be sufficient to make the foo_expr function itself externally visible, and callers would use foo_expr() to get the value instead of treating it like a variable.

Jonesinator
  • 4,186
  • 2
  • 24
  • 18