What are the (dis)advantages of using the following (A):
// .h
class SomeClass
{
static const struct ConstantGroup
{
int a = 1;
string b = "b";
// ... etc.
} CONSTANT;
};
// .cpp
const SomeClass::ConstantGroup SomeClass::CONSTANT;
Versus (B):
// .h
class SomeClass
{
static const int A;
static const string B;
// .. etc.
};
// .cpp
const int SomeClass::A = 1;
const string SomeClass::B = "b";
...for some group(s) of related static class constants? Assume no templates are involved and that the constants contain simple types (POD or strings).
So far I see the following advantages in favor of (A):
static const auto & SHORTHAND = SomeClass::LONG_NAME_FOR_CONSTANTS;
What are the disadvantages, gotcha's, or other things to keep in mind when using this pattern?
(A) might be harder to optimize by removing unnecessary variables from the final executable.
If you want to group constants, then consider using a namespace
for that purpose.
namespace ConstantGroup
{
constexpr int a = 1;
// Here best solution might depend on usage and c++ version
const std::string b;
}
Passing constants as a group really does not make much sense. If something is really constant, then you need a single definition and always use it.
Also if the constant is very specific to one class, then make it a (static) member of that class.
Interesting (following on from a conversation in the comments above with @Henri Menke about string
s and string_view
s).
Given this:
#include <string>
#include <string_view>
#include <iostream>
static const std::string a = "a";
static const std::string_view b = "b";
int main ()
{
std::cout << a << "\n";
std::cout << b << "\n";
}
You can clearly see at Godbolt that constructing a
requires a runtime initialiser whereas b
is a compile-time constant.
If you don't [like] read[ing] the code generated by the compiler, then try changing both const
s to constexpr
s. Then, std::string_view
still compiles but std::string
does not.
So, for static and / or global constant strings, constexpr std::string_view = "blah blah blah";
looks to be a good solution here as Henri says because it offers quite a bit of extra functionality over a good old-fashioned C-string, IF you can use C++17 AND you don't mind the cost of converting these to std::string
s (which will involve constructing one) in contexts where that is what is needed at that point in the code.
If not, you are forced back to std::string
or perhaps plain old C-strings.
Edit:
I noticed a strange shortcoming in std::stringview
while looking into this: it offers no operator std::string ()
method. I've no idea why not but it means, for example, that the following won't compile:
void foo (std::string s)
{
...
}
std::string_view sv = ...;
foo (sv);
This is not good enough say I, so in a spirit of sharing (if anyone's still reading at this point), I humbly offer you this:
#include <string>
#include <string_view>
template <class T> struct MyBasicStringView : public std::basic_string_view <T>
{
constexpr MyBasicStringView (const T *s) : std::basic_string_view <T> (s) { }
operator std::basic_string <T> () const { return std::basic_string <T> (this->data ()); }
};
using MyStringView = MyBasicStringView <char>;
Test program:
static constexpr MyStringView a_static_string_view = "static_string";
std::string foo (std::string s)
{
return s + " x";
}
#include <iostream>
int main ()
{
std::cout << a_static_string_view << "\n";
MyStringView sv = "abcde";
std::cout << sv << "\n";
std::cout << foo (sv) << "\n";
}
Output:
static_string
abcde
abcde x
Live demo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With