Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr array in header

I have the following code in a header file, that is included in 2 different cpp files:

constexpr int array[] = { 11, 12, 13, 14, 15 };
inline const int* find(int id)
{
    auto it = std::find(std::begin(array), std::end(array), id);
    return it != std::end(array) ? &*it : nullptr;
}

I then call find(13) in each of the cpp files. Will both pointers returned by find() point to the same address in memory?

The reason I ask is because I have similar code in my project and sometimes it works and sometimes it doesn't. I assumed both pointers would point to the same location, but I don't really have a basis for that assumption :)

like image 928
rozina Avatar asked Jun 04 '18 11:06

rozina


People also ask

Should constexpr be in header?

In other words, you should use constexpr for your constants in header files, if possible, otherwise const . And if you require the address of that constant to be the same everywhere mark it as inline .

Can std:: array be constexpr?

Since C++17 std::array can be declared as constexpr: constexpr std::array myArray{1, 2, 3}. Now starts the fun part. With C++20, you can use a std::array at compile-time. The std::array (1) and all results of the calculations are declared as constexpr .

Does constexpr imply static?

A constexpr specifier used in an object declaration or non-static member function (until C++14) implies const . A constexpr specifier used in a function or static data member (since C++17) declaration implies inline .

Is constexpr global?

constexpr implies const and const on global/namespace scope implies static (internal linkage), which means that every translation unit including this header gets its own copy of PI.


2 Answers

In C++11 and C++14:

In your example array has internal linkage (see [basic.link]/3.2), which means it will have different addresses in different translation units.

And so it's an ODR violation to include and call find in different translation units (since its definition is different).

A simple solution is to declare it extern.

In C++17:

[basic.link]/3.2 has changed such that constexpr can be inline in which case there will be no effect on the linkage anymore.

Which means that if you declare array inline it'll have external linkage and will have the same address across translation units. Of course like with any inline, it must have identical definition in all translation units.

like image 156
rustyx Avatar answered Oct 05 '22 09:10

rustyx


I can't claim to be an expert in this, but according to this blog post, the code you have there should do what you want in C++17, because constexpr then implies inline and that page says (and I believe it):

A variable declared inline has the same semantics as a function declared inline: it can be defined, identically, in multiple translation units, must be defined in every translation unit in which it is used, and the behavior of the program is as if there was exactly one variable.

So, two things to do:

  • make sure you are compiling as C++17
  • declare array as constexpr inline to force a compiler error on older compilers (and to ensure that you actually get the semantics you want - see comments below)

I believe that will do it.

like image 35
Paul Sanders Avatar answered Oct 05 '22 07:10

Paul Sanders