Carlo Wood
Carlo Wood

Reputation: 6801

How to write the constructor for a template class that wraps a container where the container can be both an array or a vector?

I wish to have a general template class that allows the user to pass the container to use:

template<class Container>
struct Sum
{
  Container m_terms;
...

Other classes will derive from that, for example,

class MySum : public Sum<std::vector<int>>
{
...

or

class MySum4 : public Sum<std::array<int, 4>>
{
...

The containers need to be initialized from the constructor. My idea at first was to use std::initializer_list, for example,

MySum ms{1, 2, 3, 4};

which would require the following constructors to be added:

template<class Container>
struct Sum
{
  Container m_terms;

  Sum(std::initializer_list<typename Container::value_type> const& il) :
    m_terms(il) { }
...
class MySum : public Sum<std::vector<int>>
{
  using Sum<std::vector<int>>::Sum;
...

but then how can I get MySum4 to work too? An array doesn't take a std::initializer_list<>.

Here is more complete code that shows the compiler error: https://wandbox.org/permlink/CZW3YaKdInwZZD8e

Upvotes: 4

Views: 215

Answers (3)

Carlo Wood
Carlo Wood

Reputation: 6801

For completeness I wanted to answer my own question with a method that I only realized later is possible; namely, simply allow the caller to pass the container (whose type it knows) as an rvalue. And to my surprise this then works with all containers when passing double braces!

struct Sum
{
  Container m_terms;

  Sum(Container&& terms) : m_terms(std::move(terms)) { }

  void print()
  {
    for (auto&& term : m_terms)
      std::cout << ' ' << term;
    std::cout << std::endl;
  }
};

And the DEMO

struct MySum : Sum<std::vector<int>>
{
  MySum(int t0, int t1, int t2, int t3) : Sum<std::vector<int>>{{t0, t1, t2, t3}} { }
};

struct MySum4 : Sum<std::array<int, 4>>
{
  MySum4(int t0, int t1, int t2, int t3) : Sum<std::array<int, 4>>{{t0, t1, t2, t3}} { }
};

struct MySumSet : Sum<std::set<int>>
{
    MySumSet(int t0, int t1, int t2, int t3) : Sum<std::set<int>>{{t0, t1, t2, t3}} { }
};

struct MySumList : Sum<std::list<int>>
{
    MySumList(int t0, int t1, int t2, int t3) : Sum<std::list<int>>{{t0, t1, t2, t3}} { }
};

int main()
{  
    MySum s(1, 2, 3, 4);
    s.print();

    MySum4 s4(1, 2, 3, 4);
    s4.print();
    static_assert(s4.m_terms.size() == 4);

    MySumSet ss(1, 2, 3, 4);
    ss.print();

    MySumList sl(1, 2, 3, 4);
    sl.print();
}

Upvotes: 1

Hiroki
Hiroki

Reputation: 2880

An alternative way is applying the variadic template with double braces for the ctor Sum::Sum as follows. This double braces enable us to set Container to std::array:

template<typename Container>
struct Sum
{
  Container m_terms;

  template<typename ...Args>
  Sum(Args&&... args) : m_terms{{std::forward<Args>(args)...}} { }

  void print()
  {
      for (auto&& term : m_terms){
        std::cout << ' ' << term;
      }

      std::cout << std::endl;
  }
};

Then you can do like this:

DEMO

struct MySum : Sum<std::vector<int>>
{
    MySum(int t0, int t1, int t2, int t3) : Sum<std::vector<int>>{t0, t1, t2, t3} { }
};

struct MySum4 : Sum<std::array<int, 4>>
{
    MySum4(int t0, int t1, int t2, int t3) : Sum<std::array<int, 4>>{t0, t1, t2, t3} { }
};

struct MySumSet : Sum<std::set<int>>
{
    MySumSet(int t0, int t1, int t2, int t3) : Sum<std::set<int>>{t0, t1, t2, t3} { }
};

struct MySumList : Sum<std::list<int>>
{
    MySumList(int t0, int t1, int t2, int t3) : Sum<std::list<int>>{t0, t1, t2, t3} { }
};

int main()
{  
    MySum s(1, 2, 3, 4);
    s.print();

    MySum4 s4(1, 2, 3, 4);
    s4.print();
    static_assert(s4.m_terms.size() == 4);

    MySumSet ss(1, 2, 3, 4);
    ss.print();

    MySumList sl(1, 2, 3, 4);
    sl.print();
}

Upvotes: 1

max66
max66

Reputation: 66210

What about using old C-style arrays and delegating constructors?

Something as follows

  template <typename T, std::size_t N, std::size_t ... Is>
  Sum (T const (& il)[N], std::index_sequence<Is...> const)
        : m_terms{{il[Is]...}}
   { }

  template <typename T, std::size_t N>
  Sum (T const (& il)[N]) : Sum(il, std::make_index_sequence<N>{})
   { }

But you have to add a couple of braces calling it

struct MySum : Sum<std::vector<int>>
{
  MySum(int t0, int t1, int t2, int t3) : Sum<std::vector<int>>{{t0, t1, t2, t3}} { }
}; // ..........................................................^..............^

struct MySum4 : Sum<std::array<int, 4>>
{
  MySum4(int t0, int t1, int t2, int t3) : Sum<std::array<int, 4>>{{t0, t1, t2, t3}} { }
}; // .............................................................^..............^

Upvotes: 1

Related Questions