Template specialization for subclasses of the template base class

This problem is a little hard to explain, so I'll start with an example:

I have a class template that takes a type and an integer constant as the template parameters, and I have several child classes that derive from the instances of this template:

template <class V, int i> struct Base { static void doSomething() { cout << "something " << i << endl; }; }; struct Child : public Base<int,12> { }; 

I want to use these classes with some other template (let me call it Test), which has specializations for different types. Since the behavior should be exactly the same for all classes that are obtained from any instance of the database, I want to define only one specialization Test, which processes all classes obtained from Base.

I know that I cannot specialize directly in Base <V, I> because this will not define child classes. Instead, my first approach used Boost enable_if and type functions:

 // empty body to trigger compiler error for unsupported types template <class T, class Enabled = void> struct Test { }; // specialization for ints, // in my actual code, I have many more specializations here template <class Enabled> struct Test <int, Enabled> { static void test (int dst) { cout << "Test<int>::test(" << dst << ")" << endl; } }; // this should handle all subclasses of Base, // but it doesn't compile template <class T, class V, int i> struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type> { static void test (const T &dst) { dst.doSomething(); } }; int main (int argc, char **argv) { Test <int>::test (23); Test <Child>::test (Child()); return 0; } 

The idea was that specialization should handle all classes that are derived from Base with any arbitrary values โ€‹โ€‹of V and i. This does not work, gcc complains:

  error: template parameters not used in partial specialization:
 error: 'V'
 error: 'i'

I suppose the problem is that this approach will require the compiler to try all possible combinations of V and i to check if any of them match. At the moment, I was working on a problem by adding something to the base class:

 template <class V, int i> struct Base { typedef V VV; static constexpr int ii = i; static void doSomething() { cout << "something " << i << endl; }; }; 

Thus, the specialization no longer needs V and I as free template parameters:

 template <class T> struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> { static void test (const T &dst) { dst.doSomething(); } }; 

And then it compiles.

Now, my question is: How can I do this without changing the base class? In this case, it was possible because I wrote it myself, but what can I do if I have to process the third one in my test pattern? Is there a more elegant solution?

Edit: Also, can someone give me a detailed explanation why exactly the first approach does not work? I have a general idea, but I would rather have a proper understanding. :-)

+6
source share
2 answers

A simple solution is to let Base inherit another Base_base :

 struct Base_base {}; template <class V, int i> struct Base : public Base_base { static void doSomething() { cout << "something " << i << endl; }; }; template <class T> struct Test <T, typename enable_if <is_base_and_derived <Base_base, T>>::type> { static void test (const T &dst) { dst.doSomething(); } }; 

[ Edited ] in third-party code, you can use a trick like:

 template <class V, int i> struct Base3rdparty { static void doSomething() { cout << "something " << i << endl; }; }; template <class V, int i> struct Base : public Base3rdparty<V, i> { typedef V VV; static constexpr int ii = i; }; template <class T> struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> { static void test (const T &dst) { dst.doSomething(); } }; 
+3
source

Use the overload function and decltype :

 // Never defined: template<typename T> std::false_type is_Base(T&); template<class V, int I> std::true_type is_Base(Base<V,I>&); template<typename IsBase, typename T> struct TestHelper; template<typename T> struct TestHelper<std::true_type, T> { static void test(const T& dst) { dst.doSomething(); } }; template<> struct TestHelper<std::false_type, int> { static void test(int dst) { std::cout << "Test<int>::test(" << dst << ")" << std::endl; } }; // ... template<typename T> struct Test { static void test(const T& dst) { TestHelper<decltype(is_Base(std::declval<T&>())), T>::test(dst); } } 
0
source

Source: https://habr.com/ru/post/915795/


All Articles