Is it better to do:
const int MY_CONST = 20; // global constant in the program
class A {
// uses MY_CONST all over the class implementation and definition
}
or this?
const int MY_CONST = 20; // global constant in the program
template<int my_const>
class A {
//uses my_const only and never MY_CONST
};
//A<MY_CONST> used later in the program
Is one of these pattern better than the other? why?
thanks
Unless that global constant is used elsewhere outside of the class I would use neither of those approaches and make the constant a member of A:
class A {
public:
static const int MY_CONST = 20;
};
const int A::MY_CONST; // Possibly need definition also
And then use A::MY_CONST in your code.
The only time I would use a template is when you need to change the value depending on the instance for some reason.
template <int I>
class A
{
public:
static const int MY_CONST = I;
};
template <int I>
const int A<I>::MY_CONST; // Definition
Then create instances like so:
A<1> a1;
A<2> a2;
The second solution is sensible if it makes sense to instantiate e.g. A<MY_CONST + 1>, or A<0>, or any other value than MY_CONST. If, however, A is strictly designed to be used with the one value, then you don't gain anything from that. In that respect the first solution gives you everything you need.
One way to look at it is that you're introducing a dependency to A (only on a number, not on anything with behavior, but still that's a kind of dependency), and the question is whether to inject that dependency via a template parameter, or have the class pull in the dependency via a named const object.
I can see the template being useful for testing the class A -- you want to write the class to work with any value, so that you can change the value in future with confidence that you won't immediately get test failures and have to fix the bugs.
So, you could write the template and test it with lots of different values, even though no "real" program will use more than one instantiation of the template.
Obviously there are other ways to write a class whose behavior depends on an integer value, and test it. For example you could use a macro and have the test harness compile the code multiple times, or you could make MY_CONST an extern value and link the code against different object files containing different values, or you could make A store the value as a data member (even a static data member).
Which ones work for you depends how the class is going to use the value, but since a template parameter is an integer constant expression it's good for most uses that the const int is good for. You can't take a template parameter's address, is the only thing that immediately springs to mind.
I would always remove the global variable. Let's distinguish 2 cases, MY_CONST is either
an implementation detail in which case I would prefer to make it a private constant
class A
{
private:
static int const MY_CONST = 20;
};
or part of the class interface in which case I would prefer to make it a template parameter and provide it as a public member
template<int N>
class A
{
public:
static int const MY_CONST = N;
};
This way, users can not only read but also "write" (at compile-time) to MY_CONST. If users will not want to specify anything but a default value for N, you could provide a default template argument, or give a simple typedef
typedef A<20> DefaultA;
Related
to avoid complicated linker rules, C++ requires that every object has
a unique definition. That rule would be broken if C++ allowed in-class
definition of entities that needed to be stored in memory as objects.
I'm having hard time understanding this:
class Y {
const int c3 = 7; // error: not static
static int c4 = 7; // error: not const
static const float c5 = 7; // error: not integral
static const int c6 = 7;
};
How does const int c6 = 7; break following rule?
C++ requires that every object has
a unique definition.
And static const int c6 = 7; do not break it (enums too)?
Please also explain why static const float c5 = 7; is not allowed but static const int c5 = 7; is allowed?
A lot of these rules have been changing over time, so it really depends on the version of C++ you are using. Also, some of these may not be technically impossible, but the committee simply decided against them because they might be hard to implement, or are prone to errors. So you might not always get the most satisfying answer on why thing are the way they are.
Lets go over them one by one.
Const Member
class Foo
{
const int bar = 7;
};
This used to be illegal before C++11. Before that version you were only allowed to initialize static variables in their declaration. If you are still not on C++11, my condolences. More details can be found here.
Static Member
class Foo
{
static int bar = 7;
};
Another one that changed, but more recently. Before C++17 it was illegal to initialize non const static variables in their declaration. This has to do with the one definition rule. When the header containing this class is included in multiple translation units (.cpp files), which one should be responsible for initializing the value? This is why you have to place the definition somewhere in a single .cpp file.
After C++17 you are allowed to do this:
class Foo
{
inline static int bar = 7;
};
When you use inline like this it somehow figures out how to only initialize it once. More details can be found here.
Static Const Float Member
class Foo
{
static const float bar = 7.0f;
};
This has mainly to do with the unpredictability of floats when you run into floating point errors. An example:
class Foo
{
static const float bar = 0.1f;
};
Since 0.1f is impossible to represent in most floating point implementations, you won't get exactly 0.1f, but only something very close to it. This can then result in different behaviors on different systems that have slightly different implementations, causing your program to run differently depending on the system.
This gets even worse when the value is the result of a calculation, because (a*b)*c is not guaranteed to be the exact same value as a*(b*c), even though it might seem like they are.
But const static floats are still allowed when you define the value outside of the class, so technically it would all still be possible to implement. But the committee never did, probably because it would cause more problems than it would solve. This is also similar to why you are allowed to use integral types as template parameters, but not floats.
template <int i> class Foo {}; // Good
template <float f> class Bar {}; // Bad
However, the committee seems to have changed it's opinion somewhat, since we are now allowed to use floats with constexpr. So it can be done if you write this instead:
class Foo
{
static constexpr float bar = 7.0f;
};
Conclusion
All of these are actually possible in some form with the right version of C++ and the right syntax. Just keep in mind the potential issues described above, and you should be good to go.
Let go through it.
FIRST READ BELOW THEN COME TO CODE
class Y {
const int c3 = 7; // It's fine. If you write good constructor.
// static int c4 = 7; // error: not const as it is trying to initialize with object constructor which it shouldn't
static const float c5 = 7; // error: not integral as we can do it for integral type only. To correct this you can do.
static constexpr float c5 = 7; // we should use constexpr for literal type.
static const int c6 = 7; // it was fine as both types are same int and 6 where it was not the case in 1st case.
};
int main()
{
Y y;
}
First, learn these lines
a. When we create a const object of a class type, the object does not
assume its constness until after the constructor completes the object's initialization.
Thus, constructors can write to const objects during their construction.
So this will yield an error.
class Const {
public:
Const(int ii);
private:
int i;
const int ci;
};
ConstRef::ConstRef(int ii)
{ // assignments:
i = ii; // ok
ci = ii; // error: cannot assign to a const
}
**TO CORRECT THIS YOU SOULD DO**
Const::Const(int ii): i(ii), ci(ii) { } // this is how constructor should be
Let's discuss everything one by one.
Your fist member is already excellent. It should work fine as per my knowledge, and it's working for me. I think it's not working for you because your constructor is trying to initialize it in the constructor body, which you can't.
We must use the constructor initializer list to provide values for members that
are const, reference, or of a class type that does not have a default
constructor.
Let come to the static part
We say a member is associated with the class by adding the keyword static to its declaration.
The static members of a class exist outside any object.
static member functions are not bound to any object; they do not have
a this pointer. As a result, static member* functions* may not be declared as const, and we may not refer to this in the body of a static member. This restriction applies both to explicit uses of this and to implicit uses of this by calling
a nonstatic member.
IMP
Because static data members are not part of individual objects of the class type, they are not defined when we create objects of the class. As a result, they are not initialized by the class’ constructors. Moreover, in general, we may not initialize a static member inside the class.
So i think you got your second error reason. You are trying to initialize it with object constructor.
Let's go to the third one.
Ordinarily, class static members may not be initialized in the class body. However, we can provide in-class initializers for static members that have const integral
type and must do so for static members that are constexprs of literal type. The initializers must be constant expressions.
So.
static constexpr float c = 7; this is fine. You can check
I hope it' clear now. Let m know if you still got issues. Btw i skipped indirect concept too. But for depth understanding you need to learn about ``literal class`.
While static member variables can be templated in C++14 this wont work:
class SomeClass
{
public:
template<typename T>
T var = {};
};
int main()
{
SomeClass instance;
instance.var<int> = 50;
instance.var<double> = 0.1;
}
What are the reasons, that templates for variable members are not supported by the C++ standard since it should be possible in principle?
When you instantiate the class you don't know how much memory it will use. Does this class contain an int and a double? What if you write
instance.var<float> = 0.2;
instance.var<long long> = 1;
later in your code
This would make two objects of the same type SomeClass different, rendering the class concept as we understand it in c++ useless.
Also your code sample implies that var could change type during runtime, this can be done using std::variant or std::any.
It cannot be possible in principle or in practice, as the other answers explain: sizeof(SomeClass) would be impossible to compute in general, and SomeClass would no longer have any predictable or sane identity, defeating the purpose of its existence.
If there are only a select few types you wish to choose from, and you wish to change the "selected" type at runtime, perhaps a variant is what you're looking for?
#include <variant>
class SomeClass
{
public:
std::variant<int, double> var = {};
};
int main()
{
SomeClass instance;
instance.var = 50;
instance.var = 0.1;
}
(This requires C++17, but a Boost equivalent has been available for many, many years.)
It works because var will be as big as it needs to to store either an int or a double (plus some housekeeping), and this size is fixed no matter which "mode" your variant is in at any given time.
If you want to accept any type, you could use std::any, which is like a variant on drugs. The overhead is a little heavier, but if your requirements are really so relaxed then this can do the job.
But if you want multiple variables, have multiple variables.
c++ has value types with known sizes. All complete types in C++ that you can create can have their sizes calculated by the compiler based only on information at or above the line of creation within that compilation unit.
In order to do what you want, either the size of instances of a class varies with every template variable ever used in any compilation unit, or the size of instances varies over time as new elements are added.
Now you can create new data based on type, but it won't be inside the class; instead, you add a map storing the data.
using upvoid=std::unique_ptr<void, void(*)()>;
template<class T>
static upvoid make(){
return { new T, [](void*ptr){ delete static_cast<T*>(ptr); } };
}
std::map<std::type_index, upvoid> m_members;
template<class T>
T& get() {
auto it = m_members.find(typeid(T));
if (it == m_members.end()){
auto r = m_members.insert( {typeid(T), make<T>()} );
it=r.first;
}
return *it.second;
}
now foo.get<int>() allocates an int if it wasn't there, and if it was there gets it. Extra work would have to be done if you want to be able to copy instances.
This kind of mess emulates what you want, but its abstraction leaks (you can tell itmisn't a member variable). And it isn't really a template member variable, it just acts a bit like one.
Barring doing something like this, what you ask for is impossoble. And doing this as part of the language would be, quite frankly, a bad idea.
A very simple thought that got me wondering: is there anything to be gained from using the following in a class:
static const int maximum_hp{10};
Rather than
int maximum_hp() const{return 10;};
...?
As far as I can see, the first way makes it easier to override the value of maximum_hp if needed in child classes (by declaring it virtual, of course); static data members cannot be modified, however. For constant primitive types, it seems, to me, to be a better way to go about it.
There is one thing which the static member gives you that the function does not: if the value is present in the class definition like you've shown, it can be used as a constant expression (such as an array bound or a template argument). In other words, this is legal:
std::array<int, Class::maximum_hp_static_memer> a;
while this is not:
std::array<int, c.maximum_hp_function()> a;
Say I have the following example:
#include <cstdlib>
class A {
public:
static const std::size_t value = 42;
};
In short, I have (or better, want) a class A with a static const std::size_t member called value with the value 42 (determined at compile time).
Now, IIRC, this only works fine under certain circumstances. It doesn't, for example, when you take the address of A::value. In order for this to work fine in all cases you'd need to add a definition in some implementation file:
const std::size_t A::value;
However, I can't do this, because I want this file to be header-only. Another common solution is this:
class A {
public:
enum { value = 42 };
};
I don't like this solution either, because I'd like the type of A::value to be std::size_t.
What is a good solution to this problem? Preferably a small and portable solution, not something with huge macro magic like BOOST_STATIC_CONSTANT.
I'd like a solution for C++03, not C++11 (it's trivial there).
First of all, using the unsigned size_t type for numbers, you're likely to run into implicit promotion problems. So, good idea to use its corresponding signed type, which is called ptrdiff_t. Which, as it happens, is the result type of a pointer difference expression.
Also, due to changes in C++11, it’s generally a good idea to include <stddef.h> and not <cstddef>, i.e., write ::ptrdiff_t or just plain ptrdiff_t, not std::ptrdiff_t.
Now, here's how to do the header file extern linkage constant thing:
template< class Dummy >
struct A_constants_
{
static ::ptrdiff_t const value;
};
template< class Dummy >
::ptrdiff_t const A_constants_<Dummy>::value = 42;
typedef A_constants_<void> A_constants;
class A
: public A_constants
{
public:
// Whatever
};
Then you can use it like this:
foo( A::value );
There are also some other ways of doing this, but the above is about the simplest and easiest to get right.
If you want to associate some constant value with a class, here are two ways to accomplish the same goal:
class Foo
{
public:
static const size_t Life = 42;
};
class Bar
{
public:
enum {Life = 42};
};
Syntactically and semantically they appear to be identical from the client's point of view:
size_t fooLife = Foo::Life;
size_t barLife = Bar::Life;
Is there any reason other than just pure style concerns why one would be preferable to another?
The enum hack used to be necessary because many compilers didn't support in-place initialization of the value. Since this is no longer an issue, go for the other option. Modern compilers are also capable of optimizing this constant so that no storage space is required for it.
The only reason for not using the static const variant is if you want to forbid taking the address of the value: you can't take an address of an enum value while you can take the address of a constant (and this would prompt the compiler to reserve space for the value after all, but only if its address is really taken).
Additionally, the taking of the address will yield a link-time error unless the constant is explicitly defined as well. Notice that it can still be initialized at the site of declaration:
struct foo {
static int const bar = 42; // Declaration, initialization.
};
int const foo::bar; // Definition.
They're not identical:
size_t *pLife1 = &Foo::Life;
size_t *pLife2 = &Bar::Life;
One difference is that the enum defines a type that can be used as a method parameter, for example, to get better type checking. Both are treated as compile time constants by the compiler, so they should generate identical code.
static const values are treated as r-values just like enum in 99% of code you'll see. Constant r-values never have memory generated for them. The advantage enum constants is they can't become l-values in that other 1%. The static const values are type safe and allow for floats, c-strings, etc.
The compiler will make Foo::Life an l-value if it has memory associated with it. The usual way to do that is to take its address. e.g. &Foo::Life;
Here is a subtle example where GCC will use the address:
int foo = rand()? Foo::Life: Foo::Everthing;
The compiler generated code uses the addresses of Life and Everything. Worse, this only produces a linker error about the missing addresses for Foo::Life and Foo::Everything. This behavior is completely standard conforming, though obviously undesirable. There are other compiler specific ways that this can happen, and all standard conforming.
Once you have a conforming c++11 compiler the correct code will be
class Foo {
public:
constexpr size_t Life = 42;
};
This is guaranteed to always be an l-value and it's type-safe, the best of both worlds.
Well, if needed, you can take the address of a static const Member Value. You've have to declare a separate member variable of enum type to take the address of it.
Another third solution?
One subtle difference is that the enum must be defined in the header, and visible for all. When you are avoiding dependencies, this is a pain. For example, in a PImpl, adding an enum is somewhat counter-productive:
// MyPImpl.hpp
class MyImpl ;
class MyPimpl
{
public :
enum { Life = 42 } ;
private :
MyImpl * myImpl ;
}
Another third solution would be a variation on the "const static" alternative proposed in the question: Declaring the variable in the header, but defining it in the source:
// MyPImpl.hpp
class MyImpl ;
class MyPimpl
{
public :
static const int Life ;
private :
MyImpl * myImpl ;
}
.
// MyPImpl.cpp
const int MyPImpl::Life = 42 ;
Note that the value of MyPImpl::Life is hidden from the user of MyPImpl (who includes MyPImpl.hpp).
This will enable the MyPimpl author to change the value of "Life" as needed, without needing the MyPImpl user to recompile, as is the overall aim of the PImpl.