How to Declare Constexpr Extern

How to declare constexpr extern?

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

inline constexpr have external linkage?

There seems to be a little bit of confusion about what "linkage" and "inline" actually means. They are independent (orthogonal) properties of a variable, but nevertheless coupled together.

To inline a variable one declares it inline. Declaring a constexpr variable at namescope does not imply inline [1]. To declare a variable to have internal linkage one declares it static or more preferrable puts it into an anonymous namespace [2],[3]. For const and constexpr (which implies const) variables there is a special rule, which gives them internal linkage as long as they are non-inline [4].

Because constexpr variables require an immediate definition [5], you typically want them to be be inline which allows multiple (equivalent) definitions in multiple translation units:

\\ c.hpp
inline constexpr int c = 0; // define it in header

\\ a.cpp
#include "c.hpp" // c is defined in a.cpp
int a = c; // use it

\\ b.cpp
#include "c.hpp" // c is re-defined in b.cpp
int b = c; // use it

The linkage of c in that example above is external, because the special rule for const variables only applies to non-inline variables.

Note that when ommiting the inline specifier in the example makes each source file get an independent non-inline definition of c with internal linkage. It will still compile but you have to be careful to not use c in any inline functions [6].

You can put inline constexpr variables into an anonymous namespace or declare it static to make its linkage internal. If we changed the example above into

\\ c.hpp
namespace {
inline constexpr int c = 0;
};

\\ a.cpp
...

the effects would be almost the same as if ommitin the inline in the original example. Each translation unit gets its own version of the (now inlined) variable and you have to make sure that you don't use c in an inline function.

Constexpr' vs 'extern const'. Which has priority?

Using extern const in the header file only tells the compiler that the variable exists and that it is not modifiable. It doesn't tell the compiler its value which means it's not a compile-time constant anymore. If it's not a compile-time constant then it can't be used for e.g. case or as an array size.

As said by M.M in the comment, either use

const int MAX_NUMBER_OF_ROWS= 99;

or

constexpr int MAX_NUMBER_OF_ROWS= 99;

directly in the header file and it will be a compile-time constant in all translation units that include the header file.

How do I forward-declare a constexpr object at namespace scope?

The real answer is that gcc is plain wrong, clang is right. The code above should compile, and it will in gcc 4.9. Or so says this bug report.

constexpr variable at namespace scope with and without explicit inline definition

(Since the question is tagged C++17, I'll use the draft standard N4659 as the reference, which avoids complication due to modules.)

First, be aware that names in different translation units refer to the same entity only if they have external linkage ([basic.link]/9). Names with internal linkage always refer to different entities in different translation units, even if their definitions appear the same.

Therefore, we can put these definitions into three groups:

  1. internal linkage

    • can appear in multiple translation units (not UB); will define different variables in different TU
  2. external linkage with inline specifier

    • can appear in multiple translation units (not UB); will define the same variable in all TU
  3. external linkage without inline specifier

    • cannot appear in multiple translation units (UB: ODR violation)

(Definitions in the first group can be problematic if the variable is odr-used in another definition with external linkage, which might violate [basic.def.odr]/(6.2).)

The following definitions belong to the first group (internal linkage):

  • both of global_non_expl_inline (It's a non-inline variable of non-volatile const-qualified type, and has no previous declaration. Thus it matches [basic.link]/(3.2))
  • both of global_non_expl_inline_static ([basic.link]/(3.1))
  • both of global_expl_inline_explicit_static ([basic.link]/(3.1))
  • global_expl_inline_explicit_extern_but_unnamed_ns ([basic.link]/(4.1))

The following definitions belong to the second group (external linkage with inline specifier):

  • both of global_expl_inline
  • both of global_expl_inline_explicit_extern

The following definition belongs to the third group (external linkage without inline specifier):

  • bar::in_class_but_out_of_source_def

(bar::in_class_static is not defined, it can appear in multiple TU but can't be odr-used without a definition.)

C++11, Is it possible to force an instance to be extern but also a constant expression of a non-type template parameter?

So because not's in the same translation unit I needed to use extern in order to give it external linkage (sorry if I butchered that explanation also). But by defining it extern I can't define it using constexpr [...]

Per my comment, you can. It wouldn't work for you, but it's a step that helps in coming up with something that would work:

extern constexpr int i = 10;

This is perfectly valid, gives i external linkage, and makes i usable in constant expressions.

But it doesn't allow multiple definitions, so it can't work in a header file which is included in multiple translation units.

Ordinarily, the way around that is with inline:

extern inline constexpr int i = 10;

But variables cannot be declared inline in C++11.

Except... when they don't need to be declared inline because the effect has already been achieved implicitly:

struct S {
static constexpr int i = 10;
};

Now, S::i has external linkage and is usable in constant expressions!

You may not even need to define your own class for this, depending on the constant's type: consider std::integral_constant. You can write

using i = std::integral_constant<int, 10>;

and now i::value will do exactly what you want.



Related Topics



Leave a reply



Submit