struct X; // Forward declaration of X
void f1(X* px) {} // Legal: can always use a pointer
void f2(X& x) {} // Legal: can always use a reference
X f3(int); // Legal: return value in function prototype
void f4(X); // Legal: parameter in function prototype
void f5(X) {} // ILLEGAL: *definitions* require complete types
Use its methods or fields, in fact trying to dereference a variable with incomplete type
class Foo {
X *m;
void method()
{
m->someMethod(); // compiler error!
int i = m->someField; // compiler error!
}
};
When it comes to templates, there is no absolute rule: whether you can use an incomplete type as a template parameter is dependent on the way the type is used in the template.
For instance, std::vector<T> requires its parameter to be a complete type, while boost::container::vector<T> does not. Sometimes, a complete type is required only if you use certain member functions; this is the case for std::unique_ptr<T>, for example.
A well-documented template should indicate in its documentation all the requirements of its parameters, including whether they need to be complete types or not.
template <typename T>
class Foo : X<T> {} // OK as long as X is defined before
// Foo is instantiated.
Foo<int> a1; // Compiler error.
template <typename T> struct X {};
Foo<int> a2; // OK since X is now defined.
Use it to declare a member of another class template:
template <typename T>
class Foo {
X<T> m; // OK as long as X is defined before
// Foo is instantiated.
};
Foo<int> a1; // Compiler error.
template <typename T> struct X {};
Foo<int> a2; // OK since X is now defined.
Define function templates or methods using this type
template <typename T>
void f1(X<T> x) {} // OK if X is defined before calling f1
template <typename T>
X<T> f2(){return X<T>(); } // OK if X is defined before calling f2
void test1()
{
f1(X<int>()); // Compiler error
f2<int>(); // Compiler error
}
template <typename T> struct X {};
void test2()
{
f1(X<int>()); // OK since X is defined now
f2<int>(); // OK since X is defined now
}
What you cannot do with an incomplete type:
Use one of its instantiations as a base class
class Foo : X<int> {} // compiler error!
Use one of its instantiations to declare a member:
class Foo {
X<int> m; // compiler error!
};
Define functions or methods using one of its instantiations