Serge Roussak
Serge Roussak

Reputation: 1807

Instantiate a class template with another class template leads to using incomplete type

Let's say, we have the following class templates:

template<size_t S>
struct A
{
  char str[S];
};

template<typename T>
class B
{
  T t;

public:
  void init(); // Should initialize the 't' member in some a way.
};

Now, if I specialize the init with some non-template types as parameter of the B template, it's OK. But, I can not specialize it with the A as the parameter of the B without specifying its S value. I.e., I'd like to do something like this:

template<>
void B<A<S>>::init()
{
  // Initialize the 't.str'. For example:
  memset(t.str, 0, S);
}

Upvotes: 0

Views: 38

Answers (2)

NutCracker
NutCracker

Reputation: 12253

Since struct A should be responsible for construction and destruction of its members, I suggest you doing the following:

template <size_t S = 5>
struct A {
    A() {
        memset(str, 0, S);
    }

    char str[S];
};

and then you can have:

template<>
void B<A<>>::init()
{}

UPDATE

As @NathanOliver pointed out, default constructor for struct A is not even needed. Following would work too:

template <size_t S = 5>
struct A {
    char str[S]{};
};

and, since responsibility of initializing the char array is passed to struct A, you do not need template specialization.

Upvotes: 0

danielschemmel
danielschemmel

Reputation: 11116

To provide a partial specialization, that performs something specific to B<A<???>>, you need to actually provide such a partial specialization for the class:

#include <cstddef>
#include <cstring>
#include <iostream>

template<std::size_t S>
struct A
{
  char str[S];
};

template<typename T>
class B
{
  T t;

public:
  void init(); // Should initialize the 't' member in some a way.
};

template<std::size_t S>
class B<A<S>> {
  using T = A<S>;

  T t;

public:
  void init(); // Should initialize the 't' member in some a way.
};

template<std::size_t S>
void B<A<S>>::init()
{
  // Initialize the 't.str'. For example:
  std::memset(t.str, 0, S);
  std::cout << "Initialized B<A<" << S << ">>\n";
}

int main() {
    B<A<3>> b;
    b.init();
}

Note that there are two key differences to your code:

  1. There is a partial specialization of the class template template<std::size_t S> class B<A<S>>
  2. The init member function of that class template can now be defined as template<std::size_t> void B<A<S>>::init() { /* ... */ }

You can omit the base case definition of the class template B and replace it with template<typename T> class B; if no other instantiation is valid in your use case.

see it live

Upvotes: 2

Related Questions