I have a number of situations where I need to mutually reference nested classes e.g.
class C1;
class C2;
// class C2::S; // not allowed by the standard
template <class T> class R
{
T* t = nullptr; // The only use of T as a data member is a T*
};
class C1
{
public:
class S
{
public:
R <C2::S> r; // Fails to compile because C2 is an incomplete type
};
};
class C2
{
public:
class S
{
public:
R <C1::S> r;
};
};
This fails because C2 is incomplete and at the point where it is used and so C2::S is not known to the compiler.
The question is in two parts:
- Is there a way to make this work in C++23 ?
- If not, is there, or has there been, any proposal to the C++ committee which would help?
There are a couple of 'obvious' solutions but I'm hoping not to have to use them:
- Flatten the structure so there's no nesting. The actual examples I have are much more complex and this would make the code unreadable.
- Use void* and then cast as necessary.
I was expecting to be able to use a dummy class in place of C2::S and then map it to C2::S before using it but it appears you can't declare a class and then redefine it with 'using', e.g. the following doesn't work:
class Z; // Z first defined as a class
class C1
{
public:
class S
{
public:
R<Z> r;
};
};
class C2
{...}
using Z = C2::S; // Redefinition error (defining Z as a typedef vs. a class)
I've also tried with templates e.g.
template <class T> class TC1
{
public:
class S
{
public:
R <typename T::S> r;
};
};
template <class T> class TC2
{
public:
class S
{
public:
R <typename T::S> r;
};
};
using C1 = TC1 <TC2 <C1>>; // Error: C1 undeclared (or not in scope)
Regarding additions to the standard, I realize that allowing the use of incomplete types would never be acceptable as it would cause a lot of complexity e.g. in specifying what could and could not be used, or trying to define when different partially-defined versions of the same class are compatible or not.
I'm out of ideas. Any suggestions?
This isn't the usual situation where people wish they could write
instead of defining
A: you just want to have the nested classes refer to each other. That means that ordinary forward declarations work:Obviously this depends on
Rnot needing its template argument to be a complete type.