4

Consider the following class template:

template<class T, std::size_t S, SomeEnum = SomeEnum::NOT_DYNAMIC>
class Foo {

Where SomeEnum would be defined as

class SomeEnum { NOT_DYNAMIC, DYNAMIC };

This class has a private std::array, but based on the value passed by the user to SomeEnum, I would like to instead use std::vector. For example, if the user passes SomeEnum::DYNAMIC, I would use std::vector instead of std::array. This would be implemented through std::conditional_t and [[no_unique_address]].

I am wondering if there is a way to "remove" the non-type template parameter S, in case the user passed SomeEnum::DYNAMIC. This is to avoid having the user type a size, when it is unnecessary because the underlying contaier is std::vector. Meanwhile, I need to keep the S parameter in case the user does not pass anything to SomeEnum, because std::array requires the size too.

Is it possible with some trickery, and if not, how would I solve this?

Current thoughts:

  • Using std::conditional_t as explained,
  • Using inheritance, or using a specialization (not yet attempted or thought of)
  • Polymorphism is NOT an option
  • Why not feed container type as a parameter of template? What are other requirement of this template? Please provide various examples of useage and what is expected to be generated by template. – Marek R Nov 28 '22 at 21:57
  • 4
    Would you mind doing something like `Foo` for an array with size 5, but [`Foo`](https://en.cppreference.com/w/cpp/container/span/dynamic_extent) for a vector? – Artyer Nov 28 '22 at 21:59
  • This class represents a Mathematical vector. I thought of that option, but I don't know how much sense it would make to have a mathematical vector implemented through, say, std::map. – SomeoneWithPassion Nov 28 '22 at 21:59
  • The approach taken by the standard library in such and similar cases is to use the maximum of `std::size_t` to denote dynamic or invalid size/indices. (see e.g. `std::span`'s `std::dynamic_extent` or `std::string`'s `npos`) With C++20 you can also write your own class type to use for combined size and `SomeEnum`. – user17732522 Nov 28 '22 at 22:01
  • @Artyer That's exactly what I was looking for. I overlooked how the standard implemented std::span, and this seems a fair solution! – SomeoneWithPassion Nov 28 '22 at 22:09

1 Answers1

5

A similar situation in the standard library exists with std::span<T, N> or std::span<T>: Omitting the template parameter entirely defaults the size to std::dynamic_extent ((std::size_t) -1), which you can use to have a std::vector instead of a std::array:

template<class T, std::size_t S = std::dynamic_extent>
class Foo {
private:
    std::conditional_t<(S == std::dynamic_extent), std::vector<T>, std::array<T, S>> container;
};
Artyer
  • 31,034
  • 3
  • 47
  • 75