Based on @Igor 's comment, I have figured out a couple of workarounds for this issue. Are based on the fact that referencing Bar
within its declaration refers to this specialization of Bar
(to quote @Igor).
Note that all of these workarounds rely on the fact that baz
can be declared as a pointer. baz
not being a pointer causes a recursion issue that was mentioned by @Nir in the comments and leads to the error:
field has incomplete type 'Foo<Bar<int>, int>'
Workaround 1
Add a forward declaration of Bar
and create a template alias Bar2
. For this to compile, baz
must be a pointer:
template <template <typename> class P, typename T>
class Foo {
P<T>* baz;
};
template<typename T> class Bar;
template <typename U> using Bar2 = Bar<U>;
template <class T>
class Bar {
Foo<Bar2, T> memberFoo;
void makeFoo() {
Foo<Bar2, T>* f = new Foo<Bar2, T>();
}
};
Foo<Bar, int> globalFoo;
The forward declaration and use of a template alias forces the compiler to use the unspecialized version of Bar
when defining memberFoo
and f
, whereas it defaults to using the unspecialized version in the definition of globalFoo
.
Workaround 2
This workaround is based on @user5800314's answer. I feel no need to re-state his workaround, but I do feel that it is worth noting the reason why it works.
I have read a similar SO question about injected class names and C++11, but the important difference here is that my code does not compile on g++, whereas theirs does. I do not believe that the issue is a lack of implementation of injected class names. I believe that this workaround fixes the compilation error because using ::Bar
instead of Bar
again forces the compiler to access the global (unspecialized) version of Bar
instead of accessing the local (specialized) version of Bar
.
Workaround 3
Specify Foo
as having a class (or typename) template-parameter instead of a template template-parameter, and be explicit about which specialization is being used whenever using the Foo
template. This also requires that baz
is a pointer, and that it does not use a template type:
template <class P, typename T>
class Foo {
P* baz;
};
template <class T>
class Bar {
Foo<Bar, T> memberFoo;
void makeFoo() {
Foo<Bar, T>* f = new Foo<Bar, T>();
}
};
Foo<Bar<int>, int> globalFoo;
This workaround resolves the of potential confusion of template template-parameters by requiring that a specific class is provided to the Foo
template. This workaround may not be usable in some cases, but may be an elegant solution in others. In my case, for instance, I will not need to instantiate an instance of Foo
from outside of Bar
, so this is a very nice way of getting around the compile error.
P.S. I really would like to credit @user5800314, since his workaround does work, however I provide a different explanation here, and since the explanation I provide here is what I believe is correct, I don't feel that I can mark @user5800314 's answer as accepted.