Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

non-integral constants

I want a header file with a non-integral constant in it, e.g. a class. Note the constant does not need to be a compile-time constant.

static const std::string Ten = "10";

This compiles but is undesirable as each compilation unit now has its own copy of Ten.

const std::string Ten = "10";

This will compile but will fail with a linker error for multiply defined Ten.

constexpr std::string Ten = "10"s;

This would work but only if the strings constructor was constexpr as well. It will be but I can't count on every non-integral constant to have a constexpr constructor ... or can I?

extern const std::string Ten = "10";

This seems to work but I'm afraid I'll get a linker error if I breath on it wrong.

inline const std::string Ten( ) { return "10"; }

This has everything I want except a clean syntax. Plus now I have to refer the constant as a function call, Ten().

inline const std::string = "10";

This seems to be the ideal solution. Of course inline variables aren't allowed by the standard.

  • Is there something in the c++ standard that says the extern version should work or am I just lucky it works with GCC?
  • Is there a compelling reason not to allow inline variables?
  • Is there a better way with c++03 or will there be a better way in c++0x?
like image 879
deft_code Avatar asked Jan 28 '10 02:01

deft_code


2 Answers

You seem to have them mixed up.

You are right about

static const std::string Ten = "10"; 

version. It will "work", but it will create a separate object in each translation unit.

The version without static will have the same effect. It won't produce linker errors, but will define a separate object in each translation unit. In C++ language const objects have internal linkage by default, meaning that

const std::string Ten = "10"; // `static` is optional

is exactly equivalent to the previous version with static.

The version with extern and initializer

extern const std::string Ten = "10"; // it's a definition!

will produce a definition of an object with external linkage (it is a definition because of the presence of an initializer). This version will result in linker errors, since you'll end up with multiple definitions of an object with external linkage - a violation of ODR.

Here's how you can do it:

In order to achieve what you are trying to achieve, you have to declare your constant in the header file

extern const std::string Ten; // non-defining declaration

and then define it (with initializer) in one and only one of the implementation files

extern const std::string Ten = "10"; // definition, `extern` optional

(If the constant is pre-declared as extern, then extern in the definition is optional. Even without an explicit extern it will define a const object with external linkage.)

like image 64
AnT Avatar answered Sep 30 '22 02:09

AnT


I don't know if there's a better way in C++ but the best way in C (which will also work in C++) is one of the ones you've listed.

Have a separate compilation unit (eg,ten.cpp) holding just the data:

const std::string Ten = "10";

and a header file (eg,ten.h) declaring it so it can be used elsewhere:

extern const std::string Ten;

Then you just have to ensure any compilation unit that wants to use it include the header file (eg,ten.h), and any executable that wants to use it link with the separate compilation unit (eg,ten.o).

This gives you one copy of the variable, accessible anywhere. Of course, you could just define it in the header file as static and have one copy per compilation unit. That would simplify what files you need to have and the static would ensure there's no doubly-defined symbols. But that's not something I'd ever recommend.

I don't know why you state:

but I'm afraid I'll get a linker error if I breath on it wrong

This is accepted practice from long ago and you should know how all these things fit together if you wish to call yourself a C++ programmer (no insult intended).

like image 30
paxdiablo Avatar answered Sep 30 '22 02:09

paxdiablo