user2052436
user2052436

Reputation: 4765

Partial specialization of a nested class

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...

Upvotes: 2

Views: 187

Answers (3)

김범무
김범무

Reputation: 103

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)

template<class T>
class CWork
{
};

template<class T, int N>
class C : public CWork<T>
{
    class iterator {};
};

template<class T>
class C<T, 1> : public CWorkDefine
{
    class iterator {};
};

Upvotes: 0

Artyer
Artyer

Reputation: 40791

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)`)

Upvotes: 2

&#214;&#246; Tiib
&#214;&#246; Tiib

Reputation: 10979

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;
    ...
};

Upvotes: 4

Related Questions