Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template partial specialization with inheritance

I need partial specialization of the struct, but I'd like also use some common functionality. For example, suppose I have the next type:

template <typename A, typename B>
struct Foo  
{
    Foo& func0() { /* common actions with A and B */; return *this; }  
    void func1() { /* common actions with A and B */ }
    void func2() { /* common actions with A and B */ }
}

Then I want specialize it for one of the template parameters - for example, I want consider special case when B is int, and I want to preserve func0 and func1 behaviour exactly the same as in common Foo (or course, func0() must return my specialized Foo& for int), func2 I want rewrite (suppose that I have more efficient implementation of it for integers), and I also want add func3() only for my specialized Foo.

Of course, I can simpy write the following:

template <typename A>
struct Foo<A, int>  
{
    Foo& func0() { /* common actions with A and B */; return *this; }  
    void func1() { /* common actions with A and B */ }
    void func2() { /* actions with A and 'int' */ }
    void func3() { /* actions with A and 'int' */ }
}

but I'd like to avoid copy-paste in func0 and func1.

I also can rename common Foo to something like FooBase and simply inherit Foo from it, but in this case I can't use the common case as

Foo<float, float> a;

What methods does exist for allow me using both

Foo<float, float> a;

and

Foo<float, int> b;

without copying and pasting common Foo's code to specialization?

I'm interested in both c++11 and earlier standard compatibility.

like image 826
avtomaton Avatar asked Dec 26 '22 01:12

avtomaton


2 Answers

This seems to work for me.

template <typename A, typename B>
struct Foo;

template <typename A, typename B>
struct FooBase
{
    Foo<A, B>& func0()
    {
        cout << "FooBase:func0\n";
        return static_cast<Foo<A, B>&>(*this);
    }

    void func1() { cout << "FooBase::func1\n"; }
    void func2() { cout << "FooBase::func2\n"; }
};

template <typename A, typename B>
struct Foo : public FooBase<A, B> {
};

template <typename A>
struct Foo<A, int> : public FooBase<A, int>
{
    void func2() { cout << "Foo<A, int>::func2\n"; }
    void func3() { cout << "Foo<A, int>::func3\n"; }
};

If you wind up needing the definition of Foo within FooBase, you may need to use the CRTP trick of passing the derived class as a template parameter to FooBase, but for simple things I think the forward declaration is sufficient.

like image 101
Jeremy Roman Avatar answered Dec 28 '22 23:12

Jeremy Roman


You can use tag-dispatching:

template <typename A, typename B>
struct Foo  
{
    decltype(func0(std::is_same<B, int>{})) func0()
    {
        return func0(std::is_same<B, int>{});
    }
    void func1() { /* common actions with A and B */ }
    void func2() { /* common actions with A and B */ }
private:
    Foo& func0(std::true_type)  { func0_common(); return *this; }
    void func0(std::false_type) { func0_common(); }
    void func0_common() { /* common actions with A and B */ }
};
like image 32
template boy Avatar answered Dec 28 '22 22:12

template boy