const struct { x } vs struct { const x} [duplicate] - c++

This question already has answers here:
How does a 'const struct' differ from a 'struct'?
(5 answers)
Closed 9 years ago.
This is a rather simple question, but somehow difficult to find a simple answer for.
In C++, what is the difference between an (edit) const-modified struct variable, and a(edit:) non-const struct variable, but with the struct having all-const members? :
typedef struct mystruct {
const int x;
} t1;
const t1 s1;
vs
typedef struct {
int x;
} t2;
const t2 s2;
? (If the answer is "the same as for classes", then please either explain it for classes or link to the explanation.)

There's no such thing as a const struct. You may have seen something like this:
const struct {
int x;
} y;
This is a declaration of a variable y with struct type. The variable y is const, not the struct. You can think of it as similar to:
struct mystruct {
int x;
};
const mystruct y;
without giving the struct type a name.

There's effectively very little difference between the two objects a and b below:
struct A
{
int x, y;
};
struct B
{
const int x, y;
};
const A a; // (plus initialiser)
B b; // (plus initialiser)
(You know, of course, that other instances of A may not be const-qualified and then you have an obvious difference.)
You can't access the members any differently in the one case than in the other. But:
You will have to ensure that you initialise the members, in both cases (which I haven't done here);
const-qualifiying the type (rather than the members) affects reference-binding:
void foo(A& a);
void foo(B& b);
int main()
{
const A a;
B b;
foo(a); // Error!
foo(b); // OK
}
The same is true, of course, if you use a pointer instead of a reference. The const context only spreads to calling code when it's applied to the type rather than encapsulated within the members.

Assuming
const struct mystruct1 {
int x1;
int x2;
} s1;
and
struct mystruct2 {
const int x1;
int x2;
} s2;
For s1 you should not assign values to any members.
For s2 only the member x1 should not be assigned a value. One is free to do so for x2.
To get closer to your example, one could do:
typedef const struct mystruct1 {
int x1;
int x2;
} S1;
typedef struct mystruct2 {
const int x1;
int x2;
} S2;
S1 s1;
S2 s2;
For s1 and s2 here the same rules apply as above.
Upate:
Referring your question literally (implying the corrections as by my examples), there is no practical difference between the two construct in terms constantness of the value they carry.

Related

What are constant members in C++?

class Foo
{
public:
const int a;
const int* x;
int* const y;
Foo() : a{ 0 }, y{ new int(20) } {
x = new int(10);
}
};
int main()
{
Foo f;
// f.a = 100; // cannot
f.x = new int(100);
// f.y = new int(100); // cannot
}
When const int a is defined as fields of a class,
it is called a constant member. It must be initialized in the initializer list and cannot be changed afterwards.
How about const int* x (which is the same as int const* x) and int* const y? Which one should be called as a constant member? If "constant member" is defined as field that must be initialized in the initializer list and cannot be changed afterwards, then the constant member is y rather than x. Am I wrong here?
Edit
According to IntelliSense, y is a constant member.
OK. I am sure I am not wrong. I will delete this question shortly. Thank you for your participation!
The "const int* x" is a (non-const) pointer to a const int. Since x is non-const, it need not be initialized upon construction.
Here are some examples:
class C
{
public:
C() :
const_int(1),
const_int_again(2),
const_ptr_to_non_const_int(nullptr),
const_ptr_to_const_int(nullptr)
{}
private:
const int const_int;
int const const_int_again;
const int* ptr_to_const_int; // Doesn't need initialized
int const* ptr_to_const_int_again; // Doesn't need initialized
int* const const_ptr_to_non_const_int;
const int* const const_ptr_to_const_int;
int const* const const_ptr_to_const_int_again;
};
You may find the cdecl.org website helpful.

reference data member in const object remains modifiable unlike non-reference data members

Why is it that reference data members seem to escape the constness of their encapsulating objects?
struct A {
int &i;
int j;
};
int i = 0;
const A a{i, 0};
a.j = 1; // compile error. j is read-only
a.i = 0; // fine even though a is const.
// same is true for const reference to A
const auto& ar = a;
ar.j = 1;
ar.i = 0; // compiles
The following link offers an explanation but their scenario is a bit different (template type expansion seemingly causing it):
https://www.fluentcpp.com/2018/07/13/the-incredible-const-reference-that-isnt-const/
What is the most efficient way to enforce constness over &i?
Your misunderstanding is because
East const worst const west const best const
const int& is misleading. It should be int const&. If you always put const on the right, C++ makes more sense. const on the left is confusing.
int&const is not a thing; references are already "top level const" because they cannot be reseated.
So adding const to int& doesn't give you int const&, it does nothing.
An int const& is better called a reference-to-const, not a const-reference.
What should you do? Don't mix references and non-references in the same struct or class.
The assignment and copy semantics of reference and non-reference variables and members are not compatible. A struct with all references has reference semantics; one with all values has value semantics. And one with both is simply insane.
struct bob {
int & a;
int & b;
};
this behaves like a reference. Copy constructor duplicates what is referenced. Assignment modifies the referenced objects themselves.
struct alice {
int a;
int b;
};
this behaves like a value. Copy and assignment changes the values themselves.
A bob const is nearly indistinguishable from a bob, just as a int&const "is the same thing as a int&".
If bob propogated const via accessors, then:
struct bob {
int const& geta()const{return a;}
int& geta(){return a;}
private:
int & a;
};
safe, right? No.
const bob b0=whatever;
bob b1=b0;
b1.geta()=7;
b1 breaks your supposedly "safe" b0, because copy is from const, and copy to a non-const is legal, so b1 has access to the references in b0.
The way you solve this is via:
struct const_bob {
int const&a;
int const&b;
};
like std::vector<???>::const_iterator.
There really isn't another way that works well.
Appropriate getters.
struct A {
int& i() { return i_; }
const int& i() const { return i_; }
int j;
private:
int &i_;
};

Initialization behavior of class members of primitive type

I know there are quite a few answered questions here about this topic but I couldn't find one that fully answered my question.
Below I'm making some assumptions based on my understanding of initialization (as of C++17). It would be nice if someone could point out / correct my errors.
Given the type
struct A
{
int x;
};
creating a local variable doesn't initialize the object at all
A a; // a.x is indeterminate, accessing it is UB
However, we can enforce aggregate initialization by the following
A a{};
A a = {};
which initializes a.x to 0.
Now, given the type
struct B
{
int x;
std::string s;
};
creating a local variable default initializes the object
B b;
which results in b.s being default initialized (because it's a non-POD) but b.x being indeterminate, accessing it is still UB.
Next, we have
struct C
{
int x = 0;
std::string s;
};
creating a local variable default initializes the object
C c;
which results in C.s being default initialized (because it's a non-POD) and c.x being copy initialized, the behavior is well defined.
Finally, lets compare some types and check, whether they have identical initialization behavior. I'm assuming there is no difference between those types if I create a (default initialized) local variable (A a;).
Case A
struct A1
{
int x;
};
struct A2
{
A2() { }
int x;
};
struct A3
{
A3() = default;
int x;
};
x is never initialized, it's value is indeterminate.
Case B
struct B1
{
int x{};
};
struct B2
{
B2() : x{} { }
int x;
};
struct B3
{
B3() = default;
int x = 0;
};
x is always initialized, it's value is 0.
Case C
struct C1
{
int x;
std::string s;
};
struct C2
{
C2() { }
int x;
std::string s;
};
struct C3
{
C3() = default;
int x;
std::string s;
};
s is always default initialized, but x is never initialized, its value is indeterminate.
Are those statements correct and if not, where are the errors and what is the actual behavior?
You missed just one subtlety: for A3 or C3, writing T3 t{}; or T3() will initialize x to 0 because a default constructor defaulted on its first declaration causes value initialization to zero-initialize the object and default-initialize it.

Why can't I assign const int in struct constructor? [duplicate]

This question already has an answer here:
Unable to initialize private const member [duplicate]
(1 answer)
Closed 4 years ago.
Why can't I do this in C++?
struct SomeStruct
{
public:
SomeStruct(const int someInt)
{
m_someInt = someInt;
}
private:
const int m_someInt;
};
Should the private field just be a regular integer?
You're assigning someInt to m_someInt, which is illegal. But initialization is okay.
struct SomeStruct
{
public:
SomeStruct(const int someInt) : m_someInt(someInt)
{
}
private:
const int m_someInt;
};
More info: Constructors and member initializer lists
Value cannot be assigned to a const storage. It can be only initialized. In case of class member variable it would be done in initialization list.
struct SomeStruct
{
public:
SomeStruct(const int someInt) : m_someInt(someInt)
{
}
private:
const int m_someInt;
};
Sometimes in-class initialization is enough:
template <int Val>
struct SomeStruct
{
public:
private:
const int m_someInt = Val;
};
Usually confusion stems from the fact that programmer doesn't see difference between two cases:
// 1) Declaring storage of int object that initially contains value 5
int a = 5; // It's a declaration.
// 2) Declaring storage of int object that contains undetermined value
int b; // Declaration
b = 5; // Assigning a value to it. It's a statement.
In your case m_someInt = someInt; is a statement that expects lvalue before =, and m_someInt is not a legal lvalue because it is const.

How to zero-initialize an union?

Consider the following code:
struct T {
int a;
union {
struct {
int a;
} s1;
struct {
char b[1024];
} s2;
};
};
int main() {
T x = T();
}
Since an explicit constructor is called, the above code ends-up zero-initializing all the data members in x.
But I would like to have x zero-initialized even if an explicit is not called. To do that one idea would be to initialize the data members in their declaration, which seems to be okay for T::a. But how can I zero-initialize all the memory occupied by the union by using
the same criteria?
struct T {
int a = 0;
union {
struct {
int a;
} s1;
struct {
char b[1024];
} s2;
};
};
int main() {
T x; // I want x to be zero-initialized
}
You could zeroize using memset:
memset(&x, 0, sizeof(x));
For a union without a user-defined default constructor, value initialization is zero initialization.
However, zero-initialization of a union may not zero all memory, but only the padding and the first member. If the first member isn't the largest, you could be left with non-zero content.
Since you know that s2 is largest, you can make a default constructor that zeros it:
struct T
{
int a;
union {
int s1a;
char s2b[1024];
};
T() : a(), s2b() {}
};
And now
T x;
will be zeroed.
I would suggest to implement a constructor for T:
struct T {
int a;
union {
struct {
int a;
} s1;
struct {
char b[1024];
} s2;
};
T() : a(), s2() {} // <- proper initialisation
};
In this case, I picked the largest member of your union since it's transparent. Otherwise you could explicitly create the union itself.