Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ dynamic_cast to forward declared class template compiles, but is it safe?

The following code compiles and gives result as one would expect in (GCC and 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 { }
};

The code dispatches a call to foo in Base to bar in Derived.

As a point of reference, the following code does not compile:

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 provides the following diagnostic:

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

The C++14 standard states in the section on the One Definition Rule, the following:

5 Exactly one definition of a class is required in a translation unit if the class is used in a way that requires the class type to be complete.
[ Example: the following complete translation unit is well-formed, even though it never defines X:
struct X; // declare X as a struct type
struct X* x1; // use X in pointer formation
X* x2; // use X in pointer formation
—end example ]
[ Note: The rules for declarations and expressions describe in which contexts complete class types are required. A class type T must be complete if: (5.1) — an object of type T is defined (3.1), or
(5.2) — a non-static class data member of type T is declared (9.2), or
(5.3) — T is used as the object type or array element type in a new-expression (5.3.4), or
(5.4) — an lvalue-to-rvalue conversion is applied to a glvalue referring to an object of type T (4.1), or
(5.5) — an expression is converted (either implicitly or explicitly) to type T (Clause 4, 5.2.3, 5.2.7, 5.2.9, 5.4), or
(5.6) — an expression that is not a null pointer constant, and has type other than cv void*, is converted to the type pointer to T or reference to T using a standard conversion (Clause 4), a dynamic_cast (5.2.7) or a static_cast (5.2.9), or ...

This seems to state that the first example is not legal. This is construct ill-formed? If so, why am I not getting an error?

like image 514
ThomasMcLeod Avatar asked Jul 10 '15 05:07

ThomasMcLeod


1 Answers

EDIT: After a little thinking: Templates firstly get definied if you instantiate them. So your first code works find cause the template firstly gets defined if the compiler has reached the line where you instantiate the template class.

like image 51
user4657571 Avatar answered Sep 28 '22 00:09

user4657571