c++templatesincomplete-type

Use of incomplete types in templates


Is it legal to use an incomplete type in a template if the type is complete when the template is instantiated?

As below

#include <iostream>

struct bar;

template <typename T>
struct foo {

    foo(bar* b) : b(b) {
    }
    
     void frobnicate() {
          b->frobnicate();
     }

     T val_;
     bar* b;
};

struct bar {
     void frobnicate() {
          std::cout << "foo\n";
     }
};

int main() {
    bar b;
    foo<int> f(&b);
    f.frobnicate();
    return 0;
}

Visual Studio compiles the above without complaining. GCC issues the warning invalid use of incomplete type 'struct bar' but compiles. Clang errors out with member access into incomplete type 'bar'.


Solution

  • Clang is correct in reporting an error (as opposed to a warning or being silent about it), though MSVC's and GCC's behavior are also consistent with the standard. See @HolyBlackCat's answer for details on that.

    The code you posted is ill-formed NDR. However, what you want to do is feasible.

    You can defer the definition of template member functions the same way you would for a non-template class. Much like non-template classes, as long as these definitions requiring bar to be a complete type happen only once bar is complete, everything is fine.

    The only hiccup is that you need to explicitly mark the method as inline to avoid ODR violations in multi-TU programs, since the definition will almost certainly be in a header.

    #include <iostream>
    
    struct bar;
    
    template <typename T>
    struct foo {
    
        foo(bar* b) : b(b) {
        }
        
        inline void frobnicate();
    
        T val_;
        bar* b;
    };
    
    struct bar {
         void frobnicate() {
              std::cout << "foo\n";
         }
    };
    
    template <typename T>
    void foo<T>::frobnicate() {
         b->frobnicate();
    }
    
    int main() {
        bar b;
        foo<int> f(&b);
        f.frobnicate();
        return 0;
    }