Consider the following classes:
template <class Derived>
class BaseCRTP {
private:
friend class LinkedList<Derived>;
Derived *next = nullptr;
public:
static LinkedList<Derived> instances;
BaseCRTP() {
instances.insert(static_cast<Derived *>(this));
}
virtual ~BaseCRTP() {
instances.remove(static_cast<Derived *>(this));
}
};
struct Derived : BaseCRTP<Derived> {
int i;
Derived(int i) : i(i) {}
};
int main() {
Derived d[] = {1, 2, 3, 4};
for (const Derived &el : Derived::instances)
std::cout << el.i << std::endl;
}
I know that it is undefined behavior to access the members of Derived
in the BaseCRTP<Derived>
constructor (or destructor), since the Derived
constructor is executed after the BaseCRTP<Derived>
constructor (and the other way around for the destructors).
My question is: is it undefined behavior to cast the this
pointer to Derived *
to store it in the linked list, without accessing any of Derived
's members?
LinkedList::insert
only accesses BaseCRTP::next
.
When using -fsanitize=undefined
, I do get a runtime error for the static_cast
s, but I don't know if it's valid or not:
instances.insert(static_cast<Derived *>(this));
crt-downcast.cpp:14:26: runtime error: downcast of address 0x7ffe03417970 which does not point to an object of type 'Derived'
0x7ffe03417970: note: object is of type 'BaseCRTP<Derived>'
82 7f 00 00 00 2d 93 29 f3 55 00 00 00 00 00 00 00 00 00 00 e8 7a 41 03 fe 7f 00 00 01 00 00 00
^~~~~~~~~~~~~~~~~~~~~~~
vptr for 'BaseCRTP<Derived>'
4
3
2
1
instances.remove(static_cast<Derived *>(this));
crt-downcast.cpp:17:26: runtime error: downcast of address 0x7ffe034179b8 which does not point to an object of type 'Derived'
0x7ffe034179b8: note: object is of type 'BaseCRTP<Derived>'
fe 7f 00 00 00 2d 93 29 f3 55 00 00 a0 79 41 03 fe 7f 00 00 04 00 00 00 f3 55 00 00 08 c0 eb 51
^~~~~~~~~~~~~~~~~~~~~~~
vptr for 'BaseCRTP<Derived>'
Additionally, here's a simplified version of the LinkedList
class:
template <class Node>
class LinkedList {
private:
Node *first = nullptr;
public:
void insert(Node *node) {
node->next = this->first;
this->first = node;
}
void remove(Node *node) {
for (Node **it = &first; *it != nullptr; it = &(*it)->next) {
if (*it == node) {
*it = node->next;
break;
}
}
}
}
LinkedList
access members ofDerived
(e.g.: by using a copy or move constructor)? – DuntBaseCRTP::next
. – SteinkeLinkedList
class to my question. – SteinkeDerived
is not under construction until its base class constructor finishes, but... how does it affect that one can'tstatic_cast
to it? – Mace;
at the end and it does not supportbegin()
/end()
– Mcfarlane