3

I often need to define string constants associated with a class. Examples would be XML attributes, default filenames and many more.

In Java I would simply define something like

public static final String KEY = "attribute";

inside the class or interface.

In C++ the following would not compile:

class Example {
public:
    static const std::string KEY = "attribute";
}

Instead, I would have to write:

class Example {
public:
    static const std::string KEY;
}

const std::string Example::KEY = "attribute";

This is something I absolutely want to avoid because of its redundancy and verbosity.

The best solution I found so far is to use functions:

class Example {
public:
    static std::string KEY() const { return "attribute"; }
}

This also provides some encapsulation and flexibility. However it might seem a bit weird to use a function just to define a constant.

So my question is, does this solution have major drawbacks and if yes, what are better alternatives?

Frank Puffer
  • 8,135
  • 2
  • 20
  • 45

3 Answers3

2

In c++03, your best option is to use a function the way you are using, and this is awkward. The only thing is, why are you returning std::string and paying it's construction price, when you can return const char* const free of charge.

If, for some reason, you really have to have const std::string (again, it runs against my whole experience, so you might want to reconsider) following code works best in terms of efficiency (your current version calls std::string constructor on every call!):

class Example {
public:
    static const std::string& key() {
       static const std::string the_key = "That's my key!";
       return the_key;
    }
}

In c++11, you have several options, of which I find the best a constexpr:

class Example {
public:
    static constexpr const char* const KEY = "TheKey";
}

The other option is in-place initialization of your const string, but why pay the price of it?

class Example {
public:
    static const std::string KEY = "TheKey";
};
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • But if the op want to use std::string, which is usually preferred over char pointers, then there is no good option (yet). – Dave Doknjas Feb 25 '16 at 19:28
  • @DaveDoknjas, who told you that `const std::string` is preferred over `const char* const`? Whoever the person was, he or she was very confused. You also wrong saying that you can't initialize string the same way as in Java - of course you can. It is just less efficient. – SergeyA Feb 25 '16 at 19:30
  • 1
    The constant will typically be used more than once, and it will typically be used as a std::string. So if I define it as a char*, it has to be converted to a string everywhere it is used. – Frank Puffer Feb 25 '16 at 19:32
  • 1
    @SergeyA: You can do a survey if you want - C++ developers are moving away from pointers. Anyway, my main point is that if someone wants to work with std::string, then working with char pointers is a step backwards. – Dave Doknjas Feb 25 '16 at 19:34
  • @FrankPuffer, why? In my world, const stuff is much more often used as character sequences. – SergeyA Feb 25 '16 at 19:35
  • 1
    `return "attribute";` has the same string construction cost on each use. – user4581301 Feb 25 '16 at 19:35
  • @DaveDoknjas, you are not alone in totally missing the point. C++ is moving (in fact, moved but for the few outsiders) from using **managing** pointers. There is a crucial difference. – SergeyA Feb 25 '16 at 19:36
  • @user4581301, exactly what I was saying. – SergeyA Feb 25 '16 at 19:37
  • @SergeyA OP says it wil be typically used as a string. So it's not saving anything, really, but is actually more expensive. – Yam Marcovic Feb 25 '16 at 19:41
  • Yes, but the point seemed to have been missed. Recommend an edit to explicitly state " `"attribute"` is a `const char *` and is implicitly converted to a `std::string` when returned as a `std::string` incurring construction costs on every call." or something similar. – user4581301 Feb 25 '16 at 19:44
  • @YamMarcovic, fair enough - I missed the point that Frank is OP. I will update my answer. – SergeyA Feb 25 '16 at 19:44
  • 2
    This `static const std::string KEY = "TheKey";` would be optimal for OP's desired usage, but doesn't compile. In fact, it's what started this question. Stylistically, I'd prefer `static constexpr char KEY[] = "TheKey";` because I can get the string length with `sizeof(Example::KEY)-1` and I don't see any damage to the constness. – user4581301 Feb 25 '16 at 20:07
  • @user4581301 only that this doesn't work: http://stackoverflow.com/questions/35066894/what-are-the-benefits-of-constexpr-char-as-a-class-member You need a pointer. – SergeyA Feb 25 '16 at 20:21
  • Your last option doesn't work - it's just repeating the non-working code that the op posted. – Dave Doknjas Feb 25 '16 at 21:19
  • Rats. Worked when I tried it out. Even in the header. The big surprise for me was when I tried in in a header included by two cpp files linked into the same executable. I was sure that would generate a linker error. GCC fooling me with non-standard extensions again? – user4581301 Feb 25 '16 at 21:19
-1

Whereas with Java, classes and their static members are loaded along with the class—which means that the class as a unit, or as part of a JAR, is replaceable—in C++ it doesn't go that way. If you stick things in headers, you pay the price of not being able to replace them until you re-compile everything that included them.

That's why the idiomatic—and most beneficial—way would be to indeed declare them in the header and provide the implementation where it belongs, in the translation units, which can be linked in to whatever other binaries. So consider readjusting your outlook (I moved from C# to C++ so I had this happen to me more than once).

But you don't have to go the way of strings at all. You can create enums, which is a much more elegant and type safe way to achieve this sometimes. With enums (and specifically enum classes), you can't just pass in the wrong string. You have to pass in one of a set of valid values.

As an example you could do:

class Example {
public:
    enum class Attributes {
        One,
        Two,
        // ...
    };
}

And then, when it matters, and when you have local knowledge of what's what, you can map the enum values to actual strings or whatnot that the actual implementation requires.

Yam Marcovic
  • 7,953
  • 1
  • 28
  • 38
  • Ehhh... as for the first part (separate implementations) ever seen boost? As a matter of fact, as far as I can tell, the world is actually moving towards header-only libs at a very high pace. As for the second, ever had to **print** the value of your enum? – SergeyA Feb 25 '16 at 19:32
  • @SergeyA Aside from where you see the world heading at, or what some libraries might be using (references appreciated by the way), is there anything wrong or misguided that you see there? As for enums, in cases where printing is important (and that's far from always), I either use a map or--when there's no other options--x macros. – Yam Marcovic Feb 25 '16 at 19:35
  • @SergeyA P.S. if you're talking about libraries which use mainly templates, it's hardly an excuse: if C++ allowed us to do otherwise, it's highly likely they would, due to the obvious drawbacks. Also, with modules coming up, I wouldn't really say headers are where the future's at. – Yam Marcovic Feb 25 '16 at 19:37
  • Is boost good enough reference for you? There is a very important reason why header-only libraries for C++ is a good thing - you do not need to ship new compiled library for every version of every C++ compiler which is out there. – SergeyA Feb 25 '16 at 19:39
  • @SergeyA It's often easier to replace a dynamic library than it is to recompile everything. Either way, shipping binaries is mainly something that either package managers do, or closed-source companies do. In the first case, the compiler is often already known in advance; in the latter, you don't release your source anyway. – Yam Marcovic Feb 25 '16 at 19:44
  • You often have to ship libraries with open source software too. Compiling them might be tricky. – SergeyA Feb 25 '16 at 19:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/104573/discussion-between-yam-marcovic-and-sergeya). – Yam Marcovic Feb 25 '16 at 19:48
-4

You can define constructor of the class so that each time the class is used the constructor will automatically assign the value "attribute to the string"

class Example {
   public:
      static const std::string KEY;
      Example()
      {
          KEY="Attribute";
      }
};
Sterling Archer
  • 22,070
  • 18
  • 81
  • 118