Reputation: 7539
Let A
be a template class, containing an inner struct
. I would like to specialize the inner struct (and only that), depending on the template parameter of A
. The following code appears to correctly do the job:
#include <iostream>
template <bool rgb>
struct A {
struct colors;
A();
};
template <>
struct A<true>::colors { enum : std::size_t { red, green, blue }; };
template <>
struct A<false>::colors { enum : std::size_t { cyan, magenta, yellow, black }; };
template<bool rgb>
A<rgb>::A()
{
if (rgb) {
std::cout << "rgb true" << std::endl;
}
else {
std::cout << "rgb false" << std::endl;
}
}
int main()
{
using colors_true = A<true>::colors;
using colors_false = A<false>::colors;
A<true> at{};
A<false> af{};
std::cout << colors_true::red << std::endl;
std::cout << colors_false::yellow << std::endl;
}
See it live on Coliru.
(The constructor of A
is just to illustrate that I am only specializing A::colors
)
Now, consider the case when A
contains an additional template parameter. I would like to mimick the code above, specializing only the bool
parameter. The following code however does not compile:
#include <iostream>
template <bool rgb, int i>
struct A {
struct colors;
A();
};
template <int i>
struct A<true, i>::colors { enum : std::size_t { red, green, blue }; };
template <int i>
struct A<false, i>::colors { enum : std::size_t { cyan, magenta, yellow, black }; };
template<bool rgb, int i>
A<rgb, i>::A()
{
if (rgb) {
std::cout << "rgb true";
}
else {
std::cout << "rgb false";
}
std::cout << " i = " << i << std::endl;
}
int main()
{
using colors_true = A<true, 2>::colors;
using colors_false = A<false, 5>::colors;
A<true, 2> at{};
A<false, 5> af{};
std::cout << colors_true::red << std::endl;
std::cout << colors_false::yellow << std::endl;
}
See it live on Coliru.
The compilation error is:
main.cpp:10:20: error: invalid class name in declaration of 'class A<true, i>::colors'
10 | struct A<true, i>::colors { enum : std::size_t { red, green, blue }; };
| ^~~~~~
main.cpp:13:21: error: invalid class name in declaration of 'class A<false, i>::colors'
13 | struct A<false, i>::colors { enum : std::size_t { cyan, magenta, yellow, black }; };
| ^~~~~~
A partial specialization of A
, like in the following code
#include <iostream>
template <bool rgb, int i>
struct A {
struct colors;
A();
};
template <int i>
struct A<true, i> {
struct colors { enum : std::size_t { red, green, blue }; };
};
template <int i>
struct A<false, i> {
struct colors { enum : std::size_t { cyan, magenta, yellow, black }; };
};
template<bool rgb, int i>
A<rgb, i>::A()
{
if (rgb) {
std::cout << "rgb true";
}
else {
std::cout << "rgb false";
}
std::cout << " i = " << i << std::endl;
}
int main()
{
using colors_true = A<true, 2>::colors;
using colors_false = A<false, 5>::colors;
A<true, 2> at{};
A<false, 5> af{};
std::cout << colors_true::red << std::endl;
std::cout << colors_false::yellow << std::endl;
}
does not work either. See it live on Coliru. While the code compiles, the partial specialization of A
completely obscures the constructor A::A()
, as the output shows. In other words, in the code above the compiler picks up two partially specialized versions of A
, where the constructor is not explicitly defined.
As a workaround, I figured out one can use inheritance:
#include <iostream>
template <bool rgb>
struct colors_type;
template <>
struct colors_type<true> {
struct colors { enum : std::size_t { red, green, blue }; };
};
template <>
struct colors_type<false> {
struct colors { enum : std::size_t { cyan, magenta, yellow, black }; };
};
template <bool rgb, int i>
struct A : public colors_type<rgb> {
A();
};
template<bool rgb, int i>
A<rgb, i>::A()
{
if (rgb) {
std::cout << "rgb true";
}
else {
std::cout << "rgb false";
}
std::cout << " i = " << i << std::endl;
}
int main()
{
using colors_true = A<true, 2>::colors;
using colors_false = A<false, 5>::colors;
A<true, 2> at{};
A<false, 5> af{};
std::cout << colors_true::red << std::endl;
std::cout << colors_false::yellow << std::endl;
}
See it live on Coliru.
Why can I specialize the inner struct only when I fully specialize the template parameters of the enclosing class (first code), and not when I only partially specialize them (second code)?
Are there other possibilities to solve the problem, apart from the inheritance trick (fourth code)?
Upvotes: 2
Views: 67
Reputation: 72271
As Oliv's answer explains, the language just doesn't allow partial specialization of a nested class.
In this case, you could also get your desired results by making the inner class a type alias, without the inheritance:
template <bool rgb>
struct colors_type;
template <>
struct colors_type<true> {
enum : std::size_t { red, green, blue };
};
template <>
struct colors_type<false> {
enum : std::size_t { cyan, magenta, yellow, black };
};
template <bool rgb, int i>
struct A {
using colors = colors_type<rgb>;
A();
};
There aren't many obvious benefits or drawbacks to these two solutions for this example, but possibly in some more complicated cases one or the other might be the easier or clearer path. (Though I would guess inheritance is a bit more often useful in these more general use cases.)
Upvotes: 1
Reputation: 18041
This is the way the language is specified:
You can explicitly specialize member class of class template (see [temp.expl.spec])
You can provide a partial specialization for a class (see [temp.class.spec])
Of interest in [temp.class.spec]:
A partial specialization of a class template provides an alternative definition of the template that is used instead of the primary definition
Each class template partial specialization is a distinct template and definitions shall be provided for the members of a template partial specialization
I think that the inheritance trick you provided is the best one trick, or at least the most used one.
Upvotes: 2