2

I am making a small helper class that derives from std::array. The constructor does not inherit, obviously, and it is that which is responsible for brace-initialization; for example:

template<typename T, size_t size>
struct foo : std::array<T,size>
{
     foo(int a, int b)
     : std::array<T,size>{a,b}
     {
          //nothing goes here since constructor is just a dummy that 
          //forwards all arguments to std::array constructor
     }
}

int main()
{
     foo<int,2> myobj = {1,2}; //brace initialization calls custom constructor with inner elements as arguments
}

The amount of arguments has to match exactly, so I am leaning towards using something like a variadic function argument in the constructor (since I am not only going to be using 2 elements in the array every single time). Using this, how would I forward the variadic argument pack to the std::array constructor? I am open to other methods of brace initialization that allow forwarding to the std::array constructor.

Note: std::initializer_list requires runtime initialization, and i am looking for a compile time/constexpr compatible method. Thank you.

max66
  • 65,235
  • 10
  • 71
  • 111
Saswat Mishra
  • 185
  • 1
  • 11
  • I question the premise of deriving from `std::array`. While this has nothing to do with the question, you should know that inheriting from `std` containers is [not a good idea](https://stackoverflow.com/questions/6806173/subclass-inherit-standard-containers/7110262). – Max Langhof Aug 13 '18 at 12:04

2 Answers2

5

You can use a perfect-forwarding constructor:

template<class... U>
foo(U&&... u)
    : std::array<T, size>{std::forward<U>(u)...}
{}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Thanks, this works perfectly. I'd also like to add (for the future readers of this question), that this causes a compilation error for GCC 5.1 (the version I'm using), but works perfectly in GCC 6.0 and above. – Saswat Mishra Aug 13 '18 at 12:02
  • 1
    I think this constructor should also be marked `constexpr`. – Carlo Wood Mar 16 '23 at 13:33
0

I don't think that inheriting from a standard container is a good idea.

Anyway...

You can use variadic templates, perfect forwarding and also SFINAE to impose that the number of arguments is exactly size.

You can also make constexpr the foo constructor so you can make constexpr foo objects.

By example

#include <array>
#include <type_traits>

template <typename T, std::size_t S>
struct foo : public std::array<T, S>
 {
   template <typename ... As,
             typename std::enable_if<sizeof...(As) == S>::type * = nullptr>
   constexpr foo (As && ... as)
      : std::array<T, S>{ { std::forward<As>(as)... } }
    { }
 };

int main ()
 {
   //constexpr foo<int, 2u> myobj1 = {1}; // compilation error
   constexpr foo<int, 2u> myobj2 = {1, 2}; // compile
   //constexpr foo<int, 2u> myobj3 = {1, 2, 3}; // compilation error
 }
max66
  • 65,235
  • 10
  • 71
  • 111