Я был о gcc слишком плохого мнения. Прекрасно он всё развиртуализирует, лучше чем руками. Руками как раз не работает из-за стирания типа (при приведению к базовому типу информация теряется). Странное дело, получается развиртуализация работает https://coliru.stacked-crooked.com/a/a2ac64a3d031d058
через какой-то другой механизм, параллельный. Ей тип -- не важен. Если при ручном вызове указателя компилятор знает что там такой-то тип, что там у него const/constexpr у такого-то типа, значит там может быть только такое и никакого значения. То при автомагической развиртуализации тип уже всё равно какой, но где-то компилятор знает, что он в этом месте получается именно такой (и опять же, если вручную, то один static_cast всё ломает), потому, что знает что там this -- именно такой, и никакой другой. Но есть ещё один ньюанс -- static перед CreateThread(). Без него опять не развиртуализирует, но уже по другой причине -- вынужден генерировать функцию на все случаи жизни (со static видит, что её вызовут только с такими-то аргументами, а не с чем угодно). На самом деле, я думаю, автору вопроса -- всё равно, несколько вызовов функций мало что изменят. Просто вот ответ на вопрос, что нужно для развиртуализации: или конкретное известное, считай константное, значение this (если virtual), или ни в коем случае не утерянная информация о типе (если CRTP и т.п.) Последний случай более надёжен, только вся программа получается -- шаблон.
#include <stdio.h>
#include <stddef.h>
#include <array>
// Base class, which will be passed to CreateThread() function.
class ThreadBase
{
public:
virtual size_t stack_size() const noexcept = 0;
virtual void *stack_base() const noexcept = 0;
virtual void run() = 0;
};
static void CreateThread(ThreadBase &thread)
{
printf("creating stack of size %lu bytes at %p\n", long(thread.stack_size()), thread.stack_base());
thread.run();
}
// Particular Thread implementation, parametrized by stack size.
template <size_t SIZE> class ThreadImpl : public ThreadBase
{
private:
mutable std::array<int, SIZE> stack;
public:
constexpr ThreadImpl() : stack() {}
constexpr size_t stack_size() const noexcept /*final*/ { return SIZE; }
void* stack_base() const noexcept /*final*/ { return &stack[0]; }
void run() /*final*/ { puts("thread run()"); }
};
ThreadImpl<42> thread_info;
int main()
{
CreateThread(thread_info);
return 0;
}
[ZX]
через какой-то другой механизм, параллельный. Ей тип -- не важен. Если при ручном вызове указателя компилятор знает что там такой-то тип, что там у него const/constexpr у такого-то типа, значит там может быть только такое и никакого значения. То при автомагической развиртуализации тип уже всё равно какой, но где-то компилятор знает, что он в этом месте получается именно такой (и опять же, если вручную, то один static_cast всё ломает), потому, что знает что там this -- именно такой, и никакой другой. Но есть ещё один ньюанс -- static перед CreateThread(). Без него опять не развиртуализирует, но уже по другой причине -- вынужден генерировать функцию на все случаи жизни (со static видит, что её вызовут только с такими-то аргументами, а не с чем угодно). На самом деле, я думаю, автору вопроса -- всё равно, несколько вызовов функций мало что изменят. Просто вот ответ на вопрос, что нужно для развиртуализации: или конкретное известное, считай константное, значение this (если virtual), или ни в коем случае не утерянная информация о типе (если CRTP и т.п.) Последний случай более надёжен, только вся программа получается -- шаблон.
-
- Спасибо! - _Constantin24(17.12.2019 11:15)