Container covariance in C++ - c++

I know that C++ doesn't support covariance for containers elements, as in Java or C#. So the following code probably is undefined behavior:
#include <vector>
struct A {};
struct B : A {};
std::vector<B*> test;
std::vector<A*>* foo = reinterpret_cast<std::vector<A*>*>(&test);
Not surprisingly, I received downvotes when suggesting this a solution to another question.
But what part of the C++ standard exactly tells me that this will result in undefined behavior? It's guaranteed that both std::vector<A*> and std::vector<B*> store their pointers in a continguous block of memory. It's also guaranteed that sizeof(A*) == sizeof(B*). Finally, A* a = new B is perfectly legal.
So what bad spirits in the standard did I conjure (except style)?

The rule violated here is documented in C++03 3.10/15 [basic.lval], which specifies what is referred to informally as the "strict aliasing rule"
If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined:
the dynamic type of the object,
a cv-qualified version of the dynamic type of the object,
a type that is the signed or unsigned type corresponding to the dynamic type of the object,
a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
a char or unsigned char type.
In short, given an object, you are only allowed to access that object via an expression that has one of the types in the list. For a class-type object that has no base classes, like std::vector<T>, basically you are limited to the types named in the first, second, and last bullets.
std::vector<Base*> and std::vector<Derived*> are entirely unrelated types and you can't use an object of type std::vector<Base*> as if it were a std::vector<Derived*>. The compiler could do all sorts of things if you violate this rule, including:
perform different optimizations on one than on the other, or
lay out the internal members of one differently, or
perform optimizations assuming that a std::vector<Base*>* can never refer to the same object as a std::vector<Derived*>*
use runtime checks to ensure that you aren't violating the strict aliasing rule
[It might also do none of these things and it might "work," but there's no guarantee that it will "work" and if you change compilers or compiler versions or compilation settings, it might all stop "working." I use the scare-quotes for a reason here. :-)]
Even if you just had a Base*[N] you could not use that array as if it were a Derived*[N] (though in that case, the use would probably be safer, where "safer" means "still undefined but less likely to get you into trouble).

You are invoking the bad spirit of reinterpret_cast<>.
Unless you really know what you do (I mean not proudly and not pedantically) reinterpret_cast is one of the gates of evil.
The only safe use I know of is managing classes and structures between C++ and C functions calls.
There maybe some others however.

The general problem with covariance in containers is the following:
Let's say your cast would work and be legal (it isn't but let's assume it is for the following example):
#include <vector>
struct A {};
struct B : A { public: int Method(int x, int z); };
struct C : A { public: bool Method(char y); };
std::vector<B*> test;
std::vector<A*>* foo = reinterpret_cast<std::vector<A*>*>(&test);
foo->push_back(new C);
test[0]->Method(7, 99); // What should happen here???
So you have also reinterpret-casted a C* to a B*...
Actually I don't know how .NET and Java manage this (I think they throw an exception when trying to insert a C).

I think it'll be easier to show than tell:
struct A { int a; };
struct Stranger { int a; };
struct B: Stranger, A {};
int main(int argc, char* argv[])
{
B someObject;
B* b = &someObject;
A* correct = b;
A* incorrect = reinterpret_cast<A*>(b);
assert(correct != incorrect); // troubling, isn't it ?
return 0;
}
The (specific) issue showed here is that when doing a "proper" conversion, the compiler adds some pointer ajdustement depending on the memory layout of the objects. On a reinterpret_cast, no adjustement is performed.
I suppose you'll understand why the use of reinterpet_cast should normally be banned from the code...

Related

Is it safe to assert(sizeof(A) == sizeof(B)) when A and B are "the same"?

Suppose I have two classes that I would expect to have exact same memory layout:
struct A {
int x;
int y;
};
/* possibly more code */
struct B {
int a;
int b;
};
Is there anything in the standard that guarantees that I can safely static_assert(sizeof(A) == sizeof(B)) ?
As a weaker variant consider
struct C {
int a;
};
static_assert( sizeof(A) >= sizeof(C) ); // can this ever fail?
static_assert( sizeof(A) > sizeof(C) ); // can this ever fail?
The question was triggered by this one. Naively I would not expect any of the asserts to fail, but is this guaranteed?
A contrived counter-example:
#include <stdint.h>
struct A {
int32_t a;
int64_t b;
};
#pragma pack(4)
struct B {
int32_t a;
int64_t b;
};
static_assert(sizeof(A) == sizeof(B));
Compilation with g++ in 64-bit Linux yields:
a.cc:15:1: error: static assertion failed
static_assert(sizeof(A) == sizeof(B));
Nothing in the Standard would forbid an implementation which identified all the structures that are ever used as parts of unions, and added a random amount of padding after each element of any structure that was not used in such fashion. On the other hand, nothing would forbid an implementation from behaving in arbitrary fashion if the number of tags an implementation can handle, nor would anything forbid an implementation from imposing a limit of one.
All of those things fall into the category of things that the Standard would allow a conforming implementation to do, but which quality implementations should generally be expected to refrain from doing even if allowed by the Standard. The Standard makes no effort to forbid implementations from doing silly things, nor to guess at whether some specialized implementations might have a good reasons for processing something in an atypical fashion. Instead, it expects that compiler writers will try to fulfill the needs of their customers whether or not the Standard requires them to do so.
Yes, the standard guarantees that it is safe to assert that. The relevant term here is layout compatible.
The standard defines that term in two parts. First it defines what a common initial sequence of data members is (but only for standard-layout structs): It is the sequence of data members that are equivalent between the two structs. The standard includes an example, but I'll use a slightly different one to avoid some technicalities:
struct A { int a; char b; };
struct B { int b1; char b2; };
struct C { int x; int y; };
In that example, the common initial layout of A and B is both of their members, while for A and C it is only their first respective members. It then defines structs as layout compatible if the common initial layout is simply the whole class.
If you have instances of two different layout-compatible types, like A and B in the above example, you *can* assume they have the same size:
static_assert(sizeof(A) == sizeof(B));
However, you *cannot* (in theory) cast between them without invoking undefined behaviour, because that violates aliasing rules:
A a{1, 'a'};
B* b = reinterpret_cast<B*>(&a); // undefined behaviour!
do_something_with(b);
What you can do, subject to the usual const/volatile rules as well as rules about data members being trivial (see When is a type in c++11 allowed to be memcpyed?), is use memcpy to get data between layout-compatible structs. Of course, that wouldn't be possible of padding between members could be randomly different, as the current top answer suggests.
A a{1, 'a'};
B b;
memcpy(&b, &a, sizeof(b)); // copy from a to b
do_something_with(b);
If do_something_with takes its argument by reference and modifies it then you would then need to copy back from b to a to reflect the effect there. In practice this would usually be optimised to be what you would expect the above cast to do.
The answer by atomsymbol gives an example that appears to contradict everything above. But you asked about what was in the standard, and a #pragma that affects padding is outside of what is covered by the standard.
The only instance where you assertion can be false is when there is a difference of packing criteria. Otherwise the assertion must be true.
The compiler only has the struct definition to work out member offset so the so unless the layoĆ¹t is consistent you would not be able access the struct.

When is it safe to re-use memory from a trivially destructible object without laundering

Regarding the following code:
class One {
public:
double number{};
};
class Two {
public:
int integer{};
}
class Mixture {
public:
double& foo() {
new (&storage) One{1.0};
return reinterpret_cast<One*>(&storage)->number;
}
int& bar() {
new (&storage) Two{2};
return reinterpret_cast<Two*>(&storage)->integer;
}
std::aligned_storage_t<8> storage;
};
int main() {
auto mixture = Mixture{};
cout << mixture.foo() << endl;
cout << mixture.bar() << endl;
}
I haven't called the destructor for the types because they are trivially destructible. My understanding of the standard is that for this to be safe, we would need to launder the pointer to storage before passing it to the reinterpret_cast. However, std::optional's implementation in libstdc++ does not seem to use std::launder() and simply constructs the object right into the union storage. https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/optional.
Is my example above well defined behavior? What do I need to do to make it work? Would a union make this work?
In your code, you do need std::launder in order to make your reinterpret_cast do what you want it to do. This is a separate issue from that of re-using memory. According to the standard ([expr.reinterpret].cast]7), your expression
reinterpret_cast<One*>(&storage)
is equivalent to:
static_cast<One*>(static_cast<void*>(&storage))
However, the outer static_cast does not succeed in producing a pointer to the newly created One object because according to [expr.static.cast]/13,
if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible (6.9.2)
with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.
That is, the resulting pointer still points to the storage object, not to the One object nested within it, and using it as a pointer to a One object would violate the strict aliasing rule. You must use std::launder to force the resulting pointer to point to the One object. Or, as pointed out in the comments, you could simply use the pointer returned by placement new directly, rather than the one obtained from reinterpret_cast.
If, as suggested in the comments, you used a union instead of aligned_storage,
union {
One one;
Two two;
};
you would sidestep the pointer-interconvertibility issue, so std::launder would not be needed on account of non-pointer-interconvertibility. However, there is still the issue of re-use of memory. In this particular case, std::launder is not needed on account of re-use because your One and Two classes do not contain any non-static data members of const-qualified or reference type ([basic.life]/8).
Finally, there was the question of why libstdc++'s implementation of std::optional does not use std::launder, even though std::optional may contain classes that contain non-static data members of const-qualified or reference type. As pointed out in comments, libstdc++ is part of the implementation, and may simply elide std::launder when the implementers know that GCC will still compile the code properly without it. The discussion that led up to the introduction of std::launder (see CWG 1776 and the linked thread, N4303, P0137) seems to indicate that, in the opinion of people who understand the standard much better than I do, std::launder is indeed required in order to make the union-based implementation of std::optional well-defined in the presence of members of const-qualified or reference type. However, I am not sure that the standard text is clear enough to make this obvious, and it might be worth having a discussion about how it might be clarified.

Violating strict aliasing does not always produce a compiler warning

Strict-aliasing has kinda thrown me into a loop. Here's the code.
I have a class
#include <arpa/inet.h>
#include <net/route.h>
class Alias
{
public:
struct rtentry rt;
struct sockaddr_in *address;
void try_aliasing()
{
address = (struct sockaddr_in *)&rt.rt_dst;
address->sin_family = AF_INET;
}
};
If I use it like:
int main()
{
Alias a ;
a.try_aliasing();
return 0;
}
It shows:
warning:dereferencing pointer '<anonymous>' does break strict-aliasing rules
However if I use the class as:
int main()
{
Alias *a = new Alias();
a->try_aliasing();
return 0;
}
It compiles just fine.
Compiled both times using:
g++ a.cpp -Wall -O2
Have looked through some threads on strict-aliasing but they've failed to clear the reason for this behavior for me.
In most situations where compilers would be capable of generating "strict-aliasing" warnings, they could just as easily recognize the relationship between the actions on storage using different lvalues. The reason that the Standard doesn't require that compilers recognize access to storage using lvalues of different types is to avoid requiring them to pessimistically assume aliasing in cases where they would otherwise have no reason to expect it [and would thus have no reason to issue any warnings about it].
As written, the Standard does not recognize any circumstances in which an lvalue of non-character type can be derived from another lvalue and used to access storage as its own type. Even something like:
struct foo {int x;} s = {0};
s.x = 1;
invokes UB because it uses an lvalue of type int to access the storage of an object of type struct foo. The authors of the Standard rely upon compiler writers to recognize situations where common sense would imply that they should behave predictably without regard for whether the Standard actually requires it. I don't think any compiler is so stupid as to not recognize that an operation on s.x is actually an operation on s, and there are many other situations where the authors of the Standard thought compilers would have the sense to recognize such things without being ordered to do so.
Unfortunately, some compiler writers have come to view the rules as justification for assuming that code which looks like it would access storage of one type using lvalues of another, doesn't do so, rather than merely making such assumptions about code that doesn't look like it does so. Given something like:
void doSomehing(structs s2 *p);
void test(struct s1 *p)
{
doSomething((struct s2*)p};
}
there are at a few ways that something like test might be invoked:
1. It might receive a pointer to a `struct s1`, which `doSomething` will need to operate upon [perhaps using the Common Initial Sequence guarantee] as though it is a `struct s2`. Further, either:
1a. During the execution of `doSomething` storage accessed exclusively via pointers derived from a struct s2 like the one that was passed in will be accessed exclusively via such means, or...
1b. During the execution of `doSomething` storage accessed via things of that type will also be accessed via other unrelated means.
2. It might receive a pointer to a `struct s2`, which has been cast to a `struct s1*` for some reason in the calling code.
3. It might receive a pointer to a `struct s1`, which `doSomething` will process as though it's a `struct s1`, despite the fact that it accepts a parameter of type `struct s2`.
A compiler might observe that none of the situations whose behavior is defined by the Standard are very likely, and thus decide to issue a warning on that basis. On the other hand, the most common situation by far would be #1a, which a compiler really should be able to handle in predictable fashion, without difficulty, whether the Standard requires it or not, by ensuring that any operations on things of type struct s2 which are performed within the function get sequenced between operations on type struct s1 which precede the call, and those on type struct s1 which follow the function call. Unfortunately, gcc and clang don't do that.

Strict aliasing rule

I'm reading notes about reinterpret_cast and it's aliasing rules ( http://en.cppreference.com/w/cpp/language/reinterpret_cast ).
I wrote that code:
struct A
{
int t;
};
char *buf = new char[sizeof(A)];
A *ptr = reinterpret_cast<A*>(buf);
ptr->t = 1;
A *ptr2 = reinterpret_cast<A*>(buf);
cout << ptr2->t;
I think these rules doesn't apply here:
T2 is the (possibly cv-qualified) dynamic type of the object
T2 and T1 are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same type T3 (since C++11)
T2 is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to cast from the first member of a struct and from an element of a union to the struct/union that contains it.
T2 is the (possibly cv-qualified) signed or unsigned variant of the dynamic type of the object
T2 is a (possibly cv-qualified) base class of the dynamic type of the object
T2 is char or unsigned char
In my opinion this code is incorrect. Am I right? Is code correct or not?
On the other hand what about connect function (man 2 connect) and struct sockaddr?
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
Eg. we have struct sockaddr_in and we have to cast it to struct sockaddr. Above rules also doesn't apply, so is this cast incorrect?
Yeah, it's invalid, but not because you're converting a char* to an A*: it's because you are not obtaining a A* that actually points to an A* and, as you've identified, none of the type aliasing options fit.
You'd need something like this:
#include <new>
#include <iostream>
struct A
{
int t;
};
char *buf = new char[sizeof(A)];
A* ptr = new (buf) A;
ptr->t = 1;
// Also valid, because points to an actual constructed A!
A *ptr2 = reinterpret_cast<A*>(buf);
std::cout << ptr2->t;
Now type aliasing doesn't come into it at all (though keep reading because there's more to do!).
(live demo with -Wstrict-aliasing=2)
In reality, this is not enough. We must also consider alignment. Though the above code may appear to work, to be fully safe and whatnot you will need to placement-new into a properly-aligned region of storage, rather than just a casual block of chars.
The standard library (since C++11) gives us std::aligned_storage to do this:
using Storage = std::aligned_storage<sizeof(A), alignof(A)>::type;
auto* buf = new Storage;
Or, if you don't need to dynamically allocate it, just:
Storage data;
Then, do your placement-new:
new (buf) A();
// or: new(&data) A();
And to use it:
auto ptr = reinterpret_cast<A*>(buf);
// or: auto ptr = reinterpret_cast<A*>(&data);
All in it looks like this:
#include <iostream>
#include <new>
#include <type_traits>
struct A
{
int t;
};
int main()
{
using Storage = std::aligned_storage<sizeof(A), alignof(A)>::type;
auto* buf = new Storage;
A* ptr = new(buf) A();
ptr->t = 1;
// Also valid, because points to an actual constructed A!
A* ptr2 = reinterpret_cast<A*>(buf);
std::cout << ptr2->t;
}
(live demo)
Even then, since C++17 this is somewhat more complicated; see the relevant cppreference pages for more information and pay attention to std::launder.
Of course, this whole thing appears contrived because you only want one A and therefore don't need array form; in fact, you'd just create a bog-standard A in the first place. But, assuming buf is actually larger in reality and you're creating an allocator or something similar, this makes some sense.
The C aliasing rules from which the rules of C++ were derived included a footnote specifying that the purpose of the rules was to say when things may alias. The authors of the Standard didn't think it necessary to forbid implementations from applying the rules in needlessly restrictive fashion in cases where things don't alias, because they thought compiler writers would honor the proverb "Don't prevent the programmer from doing what needs to be done", which the authors of the Standard viewed as part of the Spirit of C.
Situations where it would be necessary to use an lvalue of an aggregate's member type to actually alias a value of the aggregate type are rare, so it's entirely reasonable that the Standard doesn't require compilers to recognize such aliasing. Applying the rules restrictively in cases that don't involve aliasing, however, would cause something like:
union foo {int x; float y;} foo;
int *p = &foo.x;
*p = 1;
or even, for that matter,
union foo {int x; float y;} foo;
foo.x = 1;
to invoke UB since the assignment is used to access the stored values of a union foo and a float using an int, which is not one of the allowed types. Any quality compiler, however, should be able to recognize that an operation done on an lvalue which is visibly freshly derived from a union foo is an access to a union foo, and an access to a union foo is allowed to affect the stored values of its members (like the float member in this case).
The authors of the Standard probably declined to make the footnote normative because doing so would require a formal definition of when an access via freshly-derived lvalue is an access to the parent, and what kinds of access patterns constitute aliasing. While most cases would be pretty clear cut, there are some corner cases which implementations intended for low-level programming should probably interpret more pessimistically than those intended for e.g. high-end number crunching, and the authors of the Standard figured that anyone who could figure out how to handle the harder cases should be able to handle the easy ones.

Is a struct of one element compatible with the element itself?

If I have the following struct:
struct Foo { int a; };
Is the code bellow conforming with the C++ Standard? I mean, can't it generate an "Undefined Behavior"?
Foo foo;
int ifoo;
foo = *reinterpret_cast<Foo*>(&ifoo);
void bar(int value);
bar(*reinterpret_cast<int*>(&foo));
auto fptr = static_cast<void(*)(...)>(&bar);
fptr(foo);
9.2/20 in N3290 says
A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.
And your Foo is a standard-layout class.
So your second cast is correct.
I see no guarantee that the first one is correct (and I've used architecture where a char had weaker alignment restriction than a struct containing just a char, on such an architecture, it would be problematic). What the standard guarantee is that if you have a pointer to int which really point to the first element of a struct, you can reinterpret_cast it back to pointer to the struct.
Likewise, I see nothing which would make your third one defined if it was a reinterpret_cast (I'm pretty sure that some ABI use different convention to pass structs and basic types, so it is highly suspicious and I'd need a clear mention in the standard to accept it) and I'm quite sure that nothing allow static_cast between pointers to functions.
As long as you access only the first element of a struct, it's considered to be safe, since there's no padding before the first member of a struct. In fact, this trick is used, for example, in the Objecive-C runtime, where a generic pointer type is defined as:
typedef struct objc_object {
Class isa;
} *id;
and in runtime, real objecs (which are still bare struct pointers) have memory layouts like this:
struct {
Class isa;
int x; // random other data as instance variables
} *CustomObject;
and the runtime accesses the class of an actual object using this method.
Foo is a plain-old-data structure, which means it contains nothing but the data you explicitely store in it. In this case: an int.
Thus the memory layout for an int and Foo are the same.
You can typecast from one to the other without problems. Whether it's a clever idea to use this kind of stuff is a different question.
PS:
This usually works, but not necessarily due to different alignment restrictions. See AProgrammer's answer.