Следующий код компилирует и дает результат, как можно было бы ожидать (GCC и clang):
template <typename T> struct Derived;
struct Base
{
template <typename T>
void foo(T * const t)
{
dynamic_cast<Derived<T> * const>(this)->bar(t);
}
};
template <typename T>
struct Derived : Base
{
void bar(T const *) const { }
};
Код отправляет вызов foo
в Base
в bar
в Derived
.
Как точка ссылки, следующий код не компилируется:
struct Derived2;
struct Base2
{
template <typename T>
void foo(T * const t)
{
dynamic_cast<Derived2 * const>(this)->bar(t);
}
};
struct Derived2 : Base2
{
template <typename T>
void bar(T const *) const { }
};
GCC предоставляет следующую диагностику:
main.cpp: In member function 'void Base2::foo(T*)':
main.cpp:126:45: error: invalid use of incomplete type 'struct Derived2'
dynamic_cast<Derived2 * const>(this)->bar(t);
^
main.cpp:119:8: note: forward declaration of 'struct Derived2'
struct Derived2;
^
Стандартные состояния С++ 14 в разделе о правиле одного определения:
5 Точно одно определение класса требуется в блоке трансляции если класс используется таким образом, чтобы тип класса был полный.
[Пример: следующая полная единица перевода хорошо сформированный, хотя он никогда не определяет X:
struct X;//объявляем X как тип структуры
struct X * x1;//использование X в образовании указателя
X * x2;//использование X в образовании указателя
-end example]
[Примечание: правила для декларации и выражения описывают, в каких контекстах полный класс типы требуются. Тип класса T должен быть полным, если: (5.1) - объект типа T определен (3.1), или
(5.2) - объявлен нестатический член данных типа типа T (9.2) или
(5.3) - T используется как тип объекта или тип элемента массива в новом выражении (5.3.4), или
(5.4) - преобразование lvalue-to-rale применяется к glvalue, относящемуся к объекту типа T (4.1), или (5.5) - выражение преобразуется (неявно или явно) в тип T (п. 4, 5.2.3, 5.2.7, 5.2.9, 5.4) или (5.6) - выражение, которое не является константой нулевого указателя и имеет тип, отличный от cv void *, преобразуется в указатель типа к T или ссылается на T с использованием стандартного преобразования (раздел 4), dynamic_cast (5.2.7 ) или static_cast (5.2.9), или...
Похоже, что первый пример не является законным. Это конструкция плохо сформирована? Если да, то почему я не получаю ошибку?