This question refers to the current C++20 draft. The quoted passages have been slightly modified from previous standard iterations, but not in relevant ways as far as I know.
I am looking for clarification on the the terms "aliasing violation" or "strict aliasing violation".
My previous impression was that these terms refer specifically to violations of the standard paragraph [basic.lval]/11:
If a program attempts to access ([defns.access]) the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:
- the dynamic type of the object,
- a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
- a
char
,unsigned char
, orstd::byte
type.If a program invokes a defaulted copy/move constructor or copy/move assignment operator for a union of type U with a glvalue argument that does not denote an object of type cv U within its lifetime, the behavior is undefined. [ Note: Unlike in C, C++ has no accesses of class type. — end note ]
The note at the end is clarified further through notes and normative references in [defns.access] to mean that only scalar types can be accessed. See also the notes section on the related cppreference page.
Therefore it seems to me that the paragraph can never apply to access of non-static members of classes through the wrong class type and that "(strict) aliasing violation" cannot apply e.g. to the following example:
struct A { void f() {}; int a; };
struct B { void f() {}; int a; };
int main() {
auto a = new A; // aligned for all non-overaligned types
B& b = reinterpret_cast<B&>(*a);
b.f(); //1
b.a = 1; //2
}
Assume that sizeof(A) == sizeof(B)
and offsetof(A, a) == offsetof(B, b)
(although I don't think either makes a difference and the latter is trivial because the classes are standard-layout). Another variation of interest would be struct A { };
, but again I think there won't be a difference.
In the case of b.f()
there is no access to any scalar object at all. According to [expr.ref], in the latter case the referenced object is supposed to be the data member a
of the B
object referenced by b
, but since that clearly doesn't exist, I suppose forming the member access might already be undefined behavior?
Both //1
and //2
should clearly be undefined behavior in some way though. I think //1
violates [class.mfct.non-static]/2, but I am not exactly sure which paragraph //2
violates exactly.
Which standard paragraphs do the two lines //1
and //2
violate exactly and is this violation considered to be covered by the terms "(strict) aliasing violation" as well?
//2
to access even in the absence of the so-called "aliasing" rules? – PureheartedA
anint
member as well and I think in that case referring to it as aliasing violation would be common, but the quoted aliasing rule is still not violated by that. – Vysefree(0)
which is explicitly specified as a no-op). With regard to #2, I've seen nothing that would suggest that C++ was not intended to retain C's ability to implicitly create objects of structure type in untyped storage by writing the members thereof without having to first use a trivial constructor upon them... – Pureheartedb.a
doesn't refer to a union member, so it can't be an lvalue "in the future": a normal lvalue must already refer to an object. Only few specific lvalues can be in the future. – Judijudicablereinterpret_cast<B&>(a)
itself looks like a potential alignment violation, as there is no reason why anA
would always be sufficiently aligned to store aB
. (And you don't even create aB
here, but you couldn't anyway.) – Judijudicableint
? If you do, you can use placement new for anint
. And if you have storage for aB
, you can create aB
. (Careful w/ automatic dtor invocation at the end of scope if you create objects that way. Here dtors are trivial so it's OK.) In that case, cast+assignment is not expected to implicitly create objects, even w/ a fixed std. (Std is known to be defective WRT lifetime.) – JudijudicableB
object and that we can therefore not access any `B´ member. My question was whether this is stated explicitly somewhere in the standard or whether it is UB by omission. (T.C. pretty much answered that in the first comment). And secondly I want to know whether this (and the function call) would be called "aliasing violations". The question results from an answer I wrote mentioning that "aliasing violations" can only apply to scalar objects, that was heavily downvoted. – Vyseb.a = 1;
accesses out of bounds (at best), so it is moot whether there is also a strict aliasing problem – Popularreinterpret_cast
I am doing here results in an unspecified value if not properly aligned (see timsong-cpp.github.io/cppwp/n4659/expr.static.cast#13). Using such a value in any way would cause undefined behavior, making my points moot. – Vysereinterpret_cast
s on class types. – Vysenew
) . IMO it'd be clearer if the question just used your second definition ofA
, since there can then be no issue with accessing out of bounds like the first version does have – Popular(*p).x
accesses*p
and the rule applies to*p
. I have no idea whatsoever about the intent of the latest changes in C++ though , so it seems we're lost in the murk for now ... – Popularb.a
via the text "the expression designates the named member of the object designated by the first expression". The first expressionb
does now designate an object (albeit an object of typeA
, notB
) , and that object does have a named membera
. So I'm not sure on what grounds T.C.'s argument of "undefined by omission" would now apply. Of course I do strongly doubt that this was the intent sinceA
's membera
might be somewhere else – Populara.a = 1;
defined don't apply tob.a = 1
– PopularB::a
. There's noB::a
in anA
. – Relent