Понижение до производного класса в конструкторе базового класса CRTP: UB или нет? - PullRequest
3 голосов
/ 06 апреля 2020

Рассмотрим следующие классы:

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;
}

Я знаю, что доступ к членам Derived в конструкторе BaseCRTP<Derived> (или деструкторе) является неопределенным, поскольку конструктор Derived выполняется после конструктора BaseCRTP<Derived> (и наоборот для деструкторов).

У меня такой вопрос: является ли неопределенным поведение приведение указателя this к Derived * для сохранения его в связанном списке без доступа к любому из членов Derived?

LinkedList::insert только доступ BaseCRTP::next.

При использовании -fsanitize=undefined я получаю ошибку времени выполнения для static_cast с, но я не знаю, допустимо ли это или не:


    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>'

Кроме того, вот упрощенная версия класса LinkedList:

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;
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...