Stack Overflow Asked by wintergreen_plaza on November 8, 2020
I’m trying to define a class whose behavior varies in two ways, currently based on two different template parameters.
Originally I thought that template specialization simply augmented the primary class with functionality, and so my thought was to partially specialize the two arguments separately, in the hopes that this would make available all combinations.
Below is an example of what I tried to do:
// specify the ways in which people can vary
enum class Weight { Skinny, Fat };
enum class Height { Short, Tall };
// primary class
template<Weight W, Height H>
struct Person
{
const char* name = "name";
void SayHello(void);
void SayGoodbye(void);
};
// first specialize how a person says hello, based on their weight
template<Height H>
struct Person<Weight::Skinny, H>
{
const char* goal = "ride a bike";
void SayHello(void) {
printf("Hello, I am skinny %s.n", name);
printf("My goal is to %s.n", goal);
}
};
template<Height H>
struct Person<Weight::Fat, H>
{
const char* show = "movies";
void SayHello(void) {
printf("Hello, I am fat %s.n", name);
printf("My favorite show is %s.n", show);
}
};
// then specialize how a person says goodbye, based on their height
template<Weight W>
struct Person<W, Height::Short>
{
void SayGoodbye(void) { printf("Goodbye, I am short %s.n", name); }
};
template<Weight W>
struct Person<W, Height::Tall>
{
void SayGoodbye(void) { printf("Goodbye, I am tall %s.n", name); }
};
This does not work, because when I try to instantiate a Person
as in
Person<Weight::Skinny, Height::Short> X;
there is no uniquely most-specialized specialization, and (from cppreference)
If more than one specialization matches, partial order rules are used to determine which specialization is more specialized. The most specialized specialization is used, if it is unique (if it is not unique, the program cannot be compiled)
The compiler says that
Clearly this is not a viable approach, but what would be a better solution?
I suspect that I am violating the single-responsibility principle somehow, but I’m not quite sure.
Any suggestions would be welcome.
Edit:
Why do you need explicit instantation / specialization in 1st place?
Ultimately, the different specializations would also manage different sets of resources; I’m not sure if that’s relevant, or a good reason.
(I recognize that what I tried to do would open the door to different specializations both declaring a variable of the same name, which I suppose is another reason this isn’t meant to work.)
The main thing is to achieve all the combinations of height/weight, and I wasn’t sure how to do that.
Edit:
Removed the solution I had added because the one from @rustyx was better.
As noted in their answer, it doesn’t allow for cross-dependencies, but in my case I think the dependencies are only one-way (Weight
may care about Height
, but Height
does not care about Weight
), so that is not an issue.
You can use inheritance to add degrees of freedom in functionality in "layers".
enum class Weight { Skinny, Fat };
enum class Height { Short, Tall };
struct PersonBase {
const char* name = "name";
};
template<Weight W>
struct PersonWeight : PersonBase {
void SayHello();
};
template<Weight W, Height H>
struct PersonHeight : PersonWeight<W> {
void SayGoodbye();
};
template<Weight W, Height H>
struct Person : PersonHeight<W, H> {
};
template<>
struct PersonWeight<Weight::Skinny> : PersonBase {
const char* goal = "ride a bike";
void SayHello() {
printf("Hello, I am skinny %s.n", name);
printf("My goal is to %s.n", goal);
}
};
template<>
struct PersonWeight<Weight::Fat> : PersonBase {
const char* show = "movies";
void SayHello() {
printf("Hello, I am fat %s.n", name);
printf("My favorite show is %s.n", show);
}
};
template<Weight W>
struct PersonHeight<W, Height::Short> : PersonWeight<W> {
void SayGoodbye() { printf("Goodbye, I am short %s.n", name); }
};
template<Weight W>
struct PersonHeight<W, Height::Tall> : PersonWeight<W> {
void SayGoodbye() { printf("Goodbye, I am tall %s.n", name); }
};
This won't work if there's a cross-dependency between the layers though. In that case if constexpr
or SFINAE might help.
Correct answer by rustyx on November 8, 2020
You can simply use an if
condition in your functions to decide what message to print, like this:
template<Weight W, Height H>
struct Person
{
void SayHello(void) {
if (W == Weight::Skinny)
std::cout << "Hello, I am skinny.n";
else
std::cout << "Hello, I am fat.n";
}
void SayGoodbye(void) {
if (H == Height::Short)
std::cout << "Goodbye, I am short.n";
else
std::cout << "Goodbye, I am tall.n";
}
};
Here's a demo.
Answered by cigien on November 8, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP