Учитывая следующий пример CRTP:
template <typename T>
int foo(T* const)
{
return 0;
}
template <typename Derived>
struct Base
{
Base() : bar(foo(static_cast<Derived*>(this)) {};
int bar;
};
struct Derived1 : Base<Derived1> {};
Является ли преобразование this
в Derived*
действительным? Я, кажется, помню, что это может быть не так, но теперь не может найти конкретных доказательств этого.
"Натуральный" тип this
на этом этапе равен Base* const
, и, конечно же, есть некоторые случаи, когда даже статическое литье указателя this
во время инициализации не в порядке, например, повышение до завершения базовой конструкции (12.7/3).
@DeadMG говорит:
существует явное исключение в стандарте w.r.t. получив это в списке инициализаторов. это для передачи указателей на себя в подобъекты.
12.6.2/12 говорит:
[Примечание. Поскольку mem-инициализатор оценивается в области конструктора, этот указатель может использоваться в списке выражений mem-initializer для обращения к инициализированному объекту. -end note]
... хотя этого недостаточно, чтобы сказать, что преобразование в Derived*
действительно.
Моя интуиция заключается в том, что на этой фазе инициализации объекта this
не указывает на экземпляр Derived
и, как таковой, даже просто указатель на него с типом Derived*
, строго говоря, UB. Это потому, что это не действительный указатель или нулевой указатель.
(Это потенциально имеет практические последствия для таких подходов, как this, хотя в этом ответе и моем примере выше все это можно было бы отбросить, просто написав static_cast<Derived*>(0)
вместо этого.)