Represents empty type in C++ class template

Viewed 219

consider the following example of a compile-time "vector".

#include <iostream>

template <int n, int...ns>
struct static_vector {
    static constexpr int value = n;
    static_vector<ns...> rest;
};

template <int n>
struct static_vector<n> {
    static constexpr int value = n;
    void* rest;
};

template <int n, class sv>
constexpr int access_nth() {
    static_assert(n >= 0, "vector size out of bound");
    if constexpr(n == 0) {
        return sv::value;
    } else {
        static_assert(!std::is_same_v<decltype(sv::rest), void *>, "vector size out of bound");
        return access_nth<n-1, decltype(sv::rest)>();
    }
}

int main()
{
    constexpr auto a = static_vector<12, 23, 34, 45>();
    constexpr int nth = access_nth<5, decltype(a)>();
    std::cout << nth << std::endl;
}

I am mostly satisfied with what we can do now: define a vector and then get the nth element out of it. The one thing I found that not satisfying is this: I have to use void * as a dummy in the base case (where the vector only holds one element and no tail...)

I tried to have a specialisation like this:

template <>
struct static_vector<> {
}

to represent the empty vector. But it seems that the compiler always rejects this definition with the following error:

<source>:16:8: error: too few template arguments for class template 'static_vector'

struct static_vector<> {

       ^

What should I do here so I can have an empty vector?

Thanks a lot.

2 Answers

But why recursion ?

You tagged C++17 so you can use template folding, so... what about as follows ?

#include <iostream>

template <int ... Is>
struct static_vector
 {
   template <std::size_t N>
   int get () const
    {
      static_assert( N < sizeof...(Is), "index out of bound" );

      std::size_t i{};
      int ret;

      ( ... , (N == i++ ? ret = Is : 0) );

      return ret;
    }
 };


int main()
 {
   constexpr auto a = static_vector<12, 23, 34, 45>();

   std::cout << a.get<3u>() << std::endl;
 }

Specializations must conform to the base template declaration. Since at least one int is required by the base template, this does not compile.

You can make this work by declaring the template to take any number of int arguments, then specializing every case that takes one or more arguments. The base declaration is then the empty case:

template <int...>
struct static_vector {
    // Instantiated only for the no-argument case
};

template <int n>
struct static_vector<n> {
    // One-argument specialization
};

template <int n, int... ns>
struct static_vector<n, ns...> {
    // Two-or-more-argument specialization
};
Related