Ivaylo Valchev
Ivaylo Valchev

Reputation: 10415

Gaining access to class members from template specialization

So, I basically want to "add" extra stuff to the class, depending on what template arguments its being used with and then aliasing it for nicer interface. Something like this:

template<typename T, std::size_t N>
struct basic_foo
{
    T data[N];
};

//what you see as comments is what i tried
template<> //template<typename T, std::size_t N>
struct basic_foo<double, 3> //: public basic_foo<T, N>
{
    void foo_fun() 
    {
        std::cout << "I'm a foo function!"; 
        for (auto i : data) std::cout << i << " ";
    }
};

template<> //template<typename T, std::size_t N>
struct basic_foo<int, 2> //: public basic_foo<T, N>
{
    void bar_fun() 
    {
        std::cout << "I'm a bar function!";
        for (auto i : data) std::cout << i << " ";
    }
};

using foo = basic_foo<double, 3>;
using bar = basic_foo<int, 2>;

int main()
{   
    foo a = { 12.0, 2.4, 3.0 };
    bar b = { 1, 2 };
}

The problem is that I can't access data in the specializations.

Is there a way to do this? Or should I rethink my structural decisions?

Upvotes: 2

Views: 990

Answers (2)

LogicStuff
LogicStuff

Reputation: 19607

Your code, as it is now does not compile, because you don't have data member in your specializations. You have tried to inherit it from the primary template, and you almost got it.

Make a basic_foo_base class (or struct) template, where you store data and functions that needn't be specialized:

template <typename T, std::size_t N>
struct basic_foo_base
{
    T data[N];
};

That was just adding _base to the name of the existing definition. Now, define basic_foo again:

template <typename T, std::size_t N>
struct basic_foo : basic_foo_base<T, N>
{
};

And this is how a specialization will look:

template <>
// instantiate the base with the same template arguments
struct basic_foo<double, 3> : public basic_foo_base<double, 3>
{
};

I hope I got everything right.

Edit: No, I didn't.

Because of inheritance, basic_foo won't be an aggregate anymore. We'll need to add some code, so the initialization:

foo a = { 12.0, 2.4, 3.0 };

is valid again. That is, defining an implicit constructor taking std::initializer_list:

basic_foo_base(std::initializer_list<T> const& il)
{
    std::copy(il.begin(), il.end(), std::begin(data));
}

And adding

using basic_foo_base::basic_foo_base;
// or, for the primary template:
using basic_foo_base<T, N>::basic_foo_base;

in all basic_foos to include that constructor to the overload resolution.

Working code

Upvotes: 2

Jarod42
Jarod42

Reputation: 217135

Alternatively to common base class which contain data, you may fully specialize:

template<>
struct basic_foo<double, 3>
{
    double data[3]; // Should be here.

    void foo_fun() 
    {
        std::cout << "I'm a foo function!"; 
        for (auto i : data) std::cout << i << " ";
    }
};

Upvotes: 1

Related Questions