简体   繁体   中英

Forward declaration of class used in function template is not compiled

I have the following code which is not compiled with gcc, but compiles perfectly with MSVC.

class B;

class A
{
    B *_parent;

public:

    template <typename T>
    void Do(T val)
    {
        _parent->DoB(val);
    }

    A(B *parent)
        :   _parent(parent)
    {
    }
};

class B : public A
{
public:

    B()
        : A(nullptr)
    {
    }

    void DoB(int val)
    {
        cout << val << endl;
    }
};

int main() {
    B b;
    A a(&b);
    a.Do(10);
    return 0;
}

Compilation error is as follows:

prog.cpp: In member function ‘void A::Do(T)’:
prog.cpp:15:10: error: invalid use of incomplete type ‘class B’
   _parent->DoB(val);
          ^~
prog.cpp:4:7: note: forward declaration of ‘class B’
 class B;
       ^

According to the similar posts, that behaviour of gcc is correct due to paragraph 14.6.9 of the Standard. But that is counterintuitive since templates should be instantiated only where used. And the only usage occurs after all classes have been defined.

My question is what are the workarounds for this issue? Because using the member template is really convenient in this case. Maybe the problem is with the architecture or something else?

You can declare the parts of A that rely on B to be known, but define them later. The declaration then looks like this

class B;

class A
{
    B *_parent;

    public:
        template <typename T> void Do(T val); /* (see below) */
        A(B *parent) :   _parent(parent) {}
};

then comes the definition of B

class B : public A { /* As before. */ };

and finally you define the missing pieces

template <typename T> void A::Do(T val)
{
    _parent->DoB(val); /* Fine, B is known. */
}

As a side note, the original code compiled with just a warning using gcc 8.

You can add extra layer:

class A
{
    B *_parent;

private:
    template <typename TB, typename T>
    static void CallDoB(TB* b, T&& val) {
        b->DoB(std::forward<T>(val));
    }
public:

    template <typename T>
    void Do(T val)
    {
        CallDoB(_parent, val);
    }

    A(B *parent) : _parent(parent) {}
};

now b-> is dependent of template and is postponed until real instantiation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM