How to partially specialize nested class without partially specializing the nesting class?
Implementation of class C
is the same for all N
.
Implementation of C::iterator
is special for N=1
.
template<class T, int N>
class C
{
class iterator;
...
};
template<class T, int N>
class C<T, N>::iterator
{
...
};
// Partial specialization doesn't compile:
template<class T>
class C<T, 1>::iterator
{
...
};
I can partially specialize class C
for N=1
, but that's a lot of code duplication...
CodePudding user response:
If you do not want to specialize whole class then just move the iterator out of class and make it template:
template<class T, int N>
class C_iterator
{
...
};
If needed make your specializations:
template<class T>
class C_iterator<T, 1>
{
...
};
Then use it in your class as iterator, if needed befriend it:
template<class T, int N>
class C
{
using iterator = C_iterator<T, N>;
friend iterator;
...
};
CodePudding user response:
The reason is that:
template<class T>
class C<T, 1>::iterator {
// ...
}
Attempts to be the definition for a member class iterator
on a partial specialisation for C
, where no such partial specialisation exists. The exact same issue would happen if you tried this with a non-static data member, a member function, or a member template: C does not allow partial specialisations where only the outer class is partially specialised.
For example, this compiles:
template<class T, int N>
class C {
class iterator; // (A)
};
template<class T>
class C<T, 1> {
class iterator; // (B)
};
template<class T, int N>
class C<T, N>::iterator {}; // Definition for entity declared at (A)
template<class T>
class C<T, 1>::iterator {}; // Definition for entity declared at (B) *not a partial template specialisation
Whereas without the partial specialisation near (B), there is nothing for the second definition to define. As a general rule of thumb, a partial specialisation can only refer to the innermost entity, so it must be a template.
(Note this has nothing to do with what kind of entity iterator
is: The same issue would have happened if iterator
was a template class and you try to partially specialise it based on N=1
)
So the simplest answer is: you can't do exactly what you want to do.
The simplest solution is what Öö Tiib's answer is: Lift the class out and make iterator
a member type alias.
For fun, you could make iterator
a template class so you can partially specialise it. You still can't partially specialise the outer class only, so I use a constraint to emulate it:
template<class T, int N>
class C
{
template<std::nullptr_t = nullptr>
class iterator;
};
template<class T, int N>
template<std::nullptr_t>
class C<T, N>::iterator
{
};
template<class T, int N>
template<std::nullptr_t dummy> requires (N==1)
class C<T, N>::iterator<dummy>
{
};
// The first is a primary definition, the second a partial specialisation
// (Could have also made them both partial specialisations, with the first `requires (N!=1)`)
CodePudding user response:
By re declare the iterator class, You can get the same result.
template<class T, int N>
class C
{
class iterator {};
};
template<class T>
class C<T, 1>
{
class iterator {};
};
It should be separated from the common working part of the class. (unless you want to rewrite it)
class CWorkDefine
{
void func() {};
};
template<class T, int N>
class C : public CWorkDefine
{
class iterator {};
};
template<class T>
class C<T, 1> : public CWorkDefine
{
class iterator {};
};
The code below is for testing.
template<class T, int N>
class C
{
public:
class iterator {};
};
// Partial specialization doesn't compile:
template<class T>
class C<T, 1>
{
public:
class iterator { public: bool specialization; };
};
void test()
{
C<int, 2>::iterator
{
};
C<int, 1>::iterator
{
.specialization = true
};
}