Reputation: 173
I am trying to implement a member function template specialization for a templated class and all derived children. It works for the class itself but not the children.
Please have a look at following example demonstrating the problem:
#include <vector>
struct MyVector : public std::vector<int> {};
struct S {
template <typename T>
int make(T value) {
return 1;
}
template <typename T>
int make(const std::vector<T>& value) {
return 2;
}
};
int main() {
S s;
// return s.make(std::vector<int>{}); // returns 2, perfekt!
return s.make(MyVector{}); // returns 1 but should return 2
}
Live: https://godbolt.org/z/PgYkKf
I searched on stackoverflow for hours but couldn't find a proper solution.. I'm grateful for any tips!
Upvotes: 2
Views: 136
Reputation: 170064
function template specialization
First order of business. This is not a specialization. It's important to get this out of the way to get at a solution. This is overloading. The two function templates overload the make
name. And overload resolution determines which is a better match after synthesizing their signatures. In your case, the two signatures the compiler can deduce are
int make(MyVector);
int make(std::vector<int> const&);
One is the identity conversion, while the other requires binding a reference to a base, which ranks worse than "identity" as far as conversions go. So the first overload gets chosen.
You have a few options before you:
Prefer a type alias like using MyVector = std::vector<int>;
. It's streight forward, matches the second overload exactly, and will make it get chosen due to partial ordering of function templates.
Add a MyVector
overload, that delegates:
int make(MyVector const& v) { return make(static_cast<MyVector::vector const&>(v)); }
Use a more sophisticated technique to control overload resolution. The standard library has a few utilites for SFINAE based control of the process. It would require changing the first overload to this:
template <typename T>
std::enable_if_t<!std::is_convertible_v<T*, std::vector<int>*>, int>
make(T value) {
return 1;
}
But this approach is overly expert friendly and will quickly not scale with many template overloads. I recommend the previous two approaches first.
Upvotes: 3