Why are static members and static constant members different in initialization? - c++

class Test
{
private:
static int data;
public:
static constexpr int MAX{ 10 }; // OK
public:
int getData() const { return data; }
void setData(int rhs) { data = rhs; }
};
int Test::data{};
I have code like above.
Why are static members and static constant members different in initialization?
Is it just a difference in grammar?

That's because in C++17, constexpr implies inline, allowing in-line definitions with initialisers. A more fair comparison would be with static inline int data{};, which looks the same.
A constexpr variable has to have an initialiser (presumably so it can be used in constant expressions). Before C++17, this means that you have to write:
class Test
{
private:
static int data;
public:
static constexpr int MAX{ 10 };
public:
int getData() const { return data; }
void setData(int rhs) { data = rhs; }
};
constexpr int Test::MAX;
int Test::data{};
to be able to ODR-use MAX (essentially "take the address of"/"bind a reference to") since inline static data members didn't exist. This is also similar for constexpr and non-constexpr static data members.

Related

static constexpr function different than global?

I cannot understand why static constexpr behaves differently then global constexpr. What am I doing wrong? Compiler error is not particularly helpful:
prog.cpp:20:17: error: ‘static constexpr int Bar::foo(const char*)’ called in a constant expression
B = Bar::foo(s)
^
prog.cpp:20:17: error: enumerator value for ‘B’ is not an integer constant
code is as follows:
#include <iostream>
constexpr int foo(const char* v)
{
return v[0];
}
struct Bar
{
static constexpr int foo(const char* v)
{
return v[0];
}
static constexpr const char* s = "abc";
enum
{
A = ::foo(s),
B = Bar::foo(s)
};
};
int main()
{
int a[Bar::A];
a[0] = Bar::A;
}
This is because the enum is part of the class declaration, whereas the definitions of the functions inside the class are logically deferred until after the class declaration. This means that as far as the enum is concerned, it can't see the definition of B::foo, and therefore it can't use a call to it as a constant expression. You can see this by putting the enum after the class:
#include <iostream>
constexpr int foo(const char* v)
{
return v[0];
}
struct Bar
{
static constexpr int foo(const char* v)
{
return v[0];
}
static constexpr const char* s = "abc";
};
enum Enum
{
A = ::foo(Bar::s),
B = Bar::foo(Bar::s)
};
which gives no error.

How to use an integer id to identify a class in a class hierarchy automatically?

For example, I have a base class A and its sub-classes B, C and so on. B and C can also has its sub-classes. The structure is a tree with root A. And each class in the tree is assigned a different integer to identify itself. There is no restriction on the integer id's values and orders. Just make sure they are different for different classes.
My question is how to do it smartly (or automatically) by using like template techniques since manual assignment is error-prone. Any way to get the id is fine, like
class A
{
public:
static const id = ...;
};
or
template<class A>
struct Id
{
enum { value = ... };
};
Easiest way is just a function
int nextId() {
static int rval = 1;
return rval++;
}
class A { public: static const id = nextId(); };
class B { public: static const id = nextId(); };
class C { public: static const id = nextId(); };
That will work so long as you do not need to use the IDs in dynamic initialization at the start of the program.
Edit: if that is not sufficient, the next step up is to do the same thing with static variables in a template. This works across compilation units, but is still dynamic initialization time.
template <typename DummyT = void>
struct CommonCounter
{
public:
static int nextId() {
static int rval = 1;
return rval ++;
}
};
template <typename T>
struct IdFor
{
static int value()
{
static int rval = CommonCounter<>::nextId();
return rval;
}
};
class A { public: static const id = IdFor<A>::get(); };
You could do something like this. This should give the same order on the same compiler. You could also modify how you key things to get a known order and detect problems at initialisation time. Simple implementation, not tested.
#include <typeinfo>
class A {
public:
virtual ~A();
static void register_type(std::type_info const& t);
int id() const;
};
template<class T>
struct DoInitA
{
DoInitA() { A::register_type(typeid(T)); }
};
class B : public A
{
static DoInitA<B> s_a_init;
public:
~B() { }
};
//
// Implementation file.
//
#include <vector>
#include <functional>
namespace {
struct TypeinfoLess {
typedef std::reference_wrapper<const std::type_info> value_type;
bool operator()(value_type const& lhs, value_type const& rhs) const {
return lhs.get().before(rhs.get());
}
};
}
typedef std::vector<std::reference_wrapper<const std::type_info>> TypeVector;
static TypeVector s_types;
static bool s_init_complete = false;
A::~A() { }
void A::register_type(std::type_info const& t)
{
static int s_counter = 0;
if (s_init_complete)
throw std::runtime_error("Late initialisation");
s_types.push_back(std::reference_wrapper<const std::type_info>(t));
}
int A::id() const
{
if (!s_init_complete) {
sort(s_types.begin(), s_types.end(), TypeinfoLess());
s_init_complete = true;
}
for (size_t i = 0; i < s_types.size(); ++i)
if (s_types[i].get() == typeid(*this)) return i;
throw std::runtime_error("Uninitialised type");
}

const * const * member variable initialization c++

I am having trouble with the initialization of this struct (simplified for example)
struct S{ const float * const * const data;};
Basically I have a buffer of buffers of floats, and I use const to ensure someone using S cannot change anything to this member (read only).
My problem is that this is complicated and hard to read to initialize, I would like to use a lambda that return an const S, and so I could initialize members in my lambda by writing the member name : s.data = ptr;
Now this code is complex and I wonder what could be a better solution.
AFAIK, having struct S{float ** data;} a const S would not protect as efficiently the content of the member, I could not modify S::data, but I could modify *S::data.
How should I do ?
Thank you
Why not just remove the last const?
struct S{ const float * const * data;};
That way you can initialize data however you like, and it still can't be used to modify anything it points to.
data itself can be modified, but should that be prevented, it should simply be private.
The recommended way is to add a constructor to S. This allows you to set the value of data in the ctor initializer list.
struct S
{
explicit S(const float *const *const d) : data(d) {}
const float * const * const data;
};
S GetS()
{
float **data = GetData();
return S(data);
}
If you want to restrict who can change S::data after it has been initialized you can box the member variable and use friendship to allow access. This requires encapsulating the data member in an additional struct that provides conversion and assignment operators.
struct Outer
{
struct S
{
private:
struct ConstBox
{
friend Outer;
ConstBox(const ConstBox& other) : data_(other.data_) {}
explicit ConstBox(const float *const *const data) : data_(data) {}
operator const float* const* () const { return data_; }
private:
ConstBox& operator=(const float * const * data)
{
data_ = data;
return *this;
}
const float * const * data_;
};
public:
S() : data(nullptr) {}
explicit S(const float *const *const d) : data(d) {}
ConstBox data;
};
S DoSomething() const
{
S s(nullptr);
auto f = []() -> S
{
S s;
s.data = new float*[10];
return s;
};
return f();
}
};
typedef Outer::S S;
void FailTest()
{
S s;
s.data = nullptr; // <-- fails
float** v1 = s.data; // <-- fails
const float** v1 = s.data; // <-- fails
// These are ok
const float* const* v2 = s.data;
}
#CaptainObvious's answer is the correct one. Write a constructor for S, taking whatever arguments it needs, and use a member initialiser rather than an assignment statement to set 'data'.
With your simplified example I would simply do:
struct S{ float const * const * const data;};
auto create_buffer() -> float const * const * {
float **buf;
/* ... compute buffer contents */
return buf;
}
S s {create_buffer()};
However, in a comment you mention that you have many members and that initializing members based on order is not sufficiently clear.
struct S { const A a; const B b; const C c; };
S s {x,y,z}; // order based, not readable enough.
const members must be initialized as part of the object's construction. You must either specify them somehow in the initializer, or you must set their value in the class, so that they are set at construction time.
Solution 1
One way to pass them during construction, but in a readable fashion is to use a second object to help initialization:
struct S_initializer { A a; B b; C c; }
struct S {
const A a; const B b; const C c;
S(S_initializer &s) : a(s.a), b(s.b), c(s.c) {}
};
S make_S() {
S_initializer s;
s.a = x;
s.b = y;
s.c = z;
return S{s};
}
The above involves some repitition, which you can avoid by just making the initialization helper object a const member of S:
struct S {
const S_initializer m;
S(S_initializer &s) : m{s} {}
};
S make_S() {
S_initializer s;
s.a = x;
s.b = y;
s.c = z;
return S{s};
}
The tradeoff is that now to access the members of S you have to have an extra .m in there:
A a = s.m.a; // versus just s.a;
Solution 2
A second method relies on a compiler extension; Although not standard C++, gcc and clang implement C99 designated initializers in C++. VC++ does not implement this.
S s { .a = x, .b = y, .c = z };

Using non-const variable inside constexpr?

Consequently to a previous question about const/non-const with ternary operator, is the following test function ok regarding to the C++11 standard :
template<bool UseConst> class MyClass
{
public:
constexpr bool test()
{
return (UseConst) ? (_constvar) : (_var);
}
protected:
int _var;
static const int _constvar;
}
The whole problem, is that _constvar is const, and _var is non-const. I would have to access these 2 data depending on the template parameter through the same function, and I would like to have a compile-time function when I use const.
Do the test() function satisfy my requirements ?
You could use SFINAE in order to "specialize" your test function. In other words, you could do something like the following:
template<bool true_false>
struct true_type
{
static char value;
};
template<>
struct true_type<false>
{
static char value[2];
};
template<bool UseConst> class MyClass
{
private:
constexpr int pre_test(const char arg) { return _constvar; }
int pre_test(char (&)[2]) const { return _var; }
public:
constexpr int test()
{
return pre_test(true_type<UseConst>::value);
}
protected:
int _var;
static const int _constvar;
};
Now, when you call MyClass::test, if UseConst is false, test will degrade to a run-time function, but when UseConst is true, you will get a compile-time function.

working with std::bitset

There is a class definition and some bool functions which test some attributes
class MemCmd
{
friend class Packet;
public:
enum Command
{
InvalidCmd,
ReadReq,
ReadResp,
NUM_MEM_CMDS
};
private:
enum Attribute
{
IsRead,
IsWrite,
NeedsResponse,
NUM_COMMAND_ATTRIBUTES
};
struct CommandInfo
{
const std::bitset<NUM_COMMAND_ATTRIBUTES> attributes;
const Command response;
const std::string str;
};
static const CommandInfo commandInfo[];
private:
bool
testCmdAttrib(MemCmd::Attribute attrib) const
{
return commandInfo[cmd].attributes[attrib] != 0;
}
public:
bool isRead() const { return testCmdAttrib(IsRead); }
bool isWrite() const { return testCmdAttrib(IsWrite); }
bool needsResponse() const { return testCmdAttrib(NeedsResponse); }
};
The question is how can I set NeedsResponse to true or false prior to calling needsResponse()
Please note that attributes is of type std::bitset
UPDATE:
I wrote this function:
void
setCmdAttrib(MemCmd::Attribute attrib, bool flag)
{
commandInfo[cmd].attributes[attrib] = flag; // ERROR
}
void setNeedsResponse(bool flag) { setCmdAttrib(NeedsResponse, flag); }
But I get this error:
error: lvalue required as left operand of assignment
From the comments:
There are two problems here
Data members that are const must be initialized in the class constructor.
If the members are const there is no way to change them later.
So, initialize (at least) the members that are supposed to have a constant value. Remove const from the members you intend to change later.