简体   繁体   中英

non-integral constants

I want a header file with a non-integral constant in it, eg 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?

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

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

The extern version is close to what you want. Here:

// in the file tenconstant.cpp
const std::string Ten = "10";

// in the file tenconstant.h
extern const std::string Ten;

// in your file
#include "tenconstant.h"

// do stuff with Ten

You need it to be defined once for the linker, which is the purpose of myconstants.cpp , but declared everywhere you use it, which is the purpose of myconstants.h . This may seem a bit unwieldy for one variable, but for a larger project, you will probably have a nice header that gets used a lot that you can stick this in.

It is a bad idea to create a static user-defined type in this way. You can't control the order of instantiation when you have multiple such UDTs. This is not a problem in a small project, but not all projects are small. It's a better idea to make your statics all plain old data types - raw pointers - and to initialize them in some appropriate way to point at the instances you need when the program starts up, or when you need them. This puts you in control.

Your question stated that these types do not need to be compile-time constants. If so, and you have a multi-threaded program, your objects need to have their state protected from simultaneous access from multiple threads. If some of your objects are not thread-safe, then in addition to the object itself you need a mutex object to protect its state, and that has to have the same linkage, and will need initialization. All this complicates the global state of your program in what could be an unacceptable way.

I think the other answers here are better, but if you're dead-set on doing it all with headers, you can effectively inline your object (as you specifically ask) with a simple wrapper function.

inline const std::string &get_ten() {
    static const std::string ten = "10";
    return ten;
}

There will be only one string , initialized once, and you don't need anything outside of the header file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM