Suppose the following structures:
// struct 'A'
struct A {
static std::string toString() { return "A"; }
};
// struct 'B' inherits 'A'
struct B : public A {
static std::string toString() { return "B"; }
};
// struct 'X'
struct X {
static std::string toString() { return "X"; }
};
then the following template class:
template <typename T> // Common 'ToString' template type:
class ToString {
public:
static void print() {
std::cout << "Other type: " << T::toString() << "\n";
}
};
and one specialization for A
:
template <>
class ToString<A> { // Specialization of 'ToString' for type 'A' or subtypes of 'A':
public:
static void print() {
std::cout << "Subtype of A: " << A::toString() << "\n";
}
};
The result of:
int main(void) {
ToString<X>::print(); // OK: invokes 'template<T> ToString::print()'
ToString<A>::print(); // OK: invokes 'ToString<A>::print()'
ToString<B>::print(); // KO: I want it invokes 'ToString<A>::print()'
}
is
Other type: X
Subtype of A: A
Other type: B
I have 2 questions:
1: Since B inherits A, why the output of ToString<B>::print()
is not something like:
Subtype of A: ...
2: In ToString<A>::print()
function, i wrote ... << A::toString() << ...
.
What can I write instead of the (by the way redundant) A
to refer to the actual type of the template type parameter which is A
itself or any of its subtypes, so that the output for B
would be:
Subtype of A: B
Thanks
CodePudding user response:
Specialization for A
requires an exact match, but A
and B
are different types. Inheritance is irrelevant here. Try to make a specialization more generic:
template<typename T, typename = std::bool_constant<true>>
struct ToString {
// ...
};
template<typename T>
struct ToString<T, std::bool_constant<std::is_base_of_v<A, T>>> {
static void print() {
std::cout << "Subtype of A: " << T::toString() << "\n";
}
};
Now the output is:
Other type: X
Subtype of A: A
Subtype of A: B
The idea is similar to std::void_t
trick, which is explained here.
Demo.
In C 20, you can also do this:
template<typename T>
struct ToString {
// ...
};
template<std::derived_from<A> T>
struct ToString<T> {
static void print() {
std::cout << "Subtype of A: " << T::toString() << "\n";
}
};