2

I want to combine many vectors to one using a template function with varidic arguments. I have a problem with three or more vectors, below my current code:

#include <iostream>
#include <vector>
#include <numeric>

template<typename _Ty, typename ..._Args>
std::vector<_Ty> combine(const std::vector<_Ty> &a, const _Args &...args) {
  // Determine size of new vector
  std::vector<std::size_t> sizes = { a.size(), args.size()... };
  std::size_t size = std::accumulate(sizes.begin(), sizes.end(), 0);
  // Create vector with new size
  std::vector<_Ty> result(size);
  // Insert all vectors into this one
  result.insert(a.begin(), a.end(), result.end());
  result.insert(result.end(), args.begin()..., args.end()...);
  return result;
}

int main(int argc, char *argv[], char *envp[]) {
  std::vector<int> a = { 0, 1, 2, 3, 4 };
  std::vector<int> b = { 4, 3, 2, 1, 0 };
  std::vector<int> c = { 1, 1, 1, 1 };

  std::cout << combine(a, b).size() << std::endl;
  std::cout << combine(a, b, c).size() << std::endl; // <-- Does not compile

  std::cin.ignore();

  return 0;
}  

So the exact problem is combine(a, b, c) does not compile. I know why. Because this line:

result.insert(result.end(), args.begin()..., args.end()...);

Get compiled to:

result.insert(result.end(), b.begin(), c.begin(), b.end(), c.end());

But I don´t know how to call result.insert with varidic arguments so it would compiled to:

result.insert(result.end(), b.begin(), b.end());
result.insert(result.end(), c.begin(), c.end());

One possibility would be:

std::vector<std::vector<_Ty> all = { a, args...};
for (const auto &vec : all) {
    result.insert(vec.begin(), vec.end());
}

But this would need a second copy of all vectors.... any idea? Thank you!

Viatorus
  • 1,804
  • 1
  • 18
  • 41

1 Answers1

4

The standard trick is to just use something like expander:

template<typename _Ty, typename ..._Args>
std::vector<_Ty> combine(std::vector<_Ty> a, const _Args &...args)
                      // ^^^^^^^^^^^^^^^^^^ by-value
{
    using expander = int[];
    expander{0,
        (void(a.insert(a.end(), args.begin(), args.end())), 0)...
    };
    return a;
}

Side-note, _Ty and _Args are reserved names.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    @PaoloM Check out T.C.'s answer [here](http://stackoverflow.com/a/25683817/2069064). There may be better explanations out there, but that's the first one I've found that I like. – Barry Aug 21 '15 at 13:20
  • @Barry Is the order defined in which the initializers are evaluated? – ex-bart Aug 21 '15 at 13:35
  • To clarify: It is defined for list-initialization (C++11 §8.5.4[dcl.init.list]/4), but this is aggregate initialization. – ex-bart Aug 21 '15 at 13:40
  • 1
    @ex-bart Aggregate initialization is a kind of list initialization. – Barry Aug 21 '15 at 13:42
  • Hmm, it probably is defined. I guess the standard should be interpreted as "aggregate initialization is a special case of list initialization, and so the provisions under list initialization apply to aggregate initialization too". Aggregate initialization may have its own clause separate from list initialization, but that is just for historical reasons, it does not signify anything regarding this matter. I'm still not 100% convinced, but close enough. – ex-bart Aug 21 '15 at 14:30
  • 1
    @ex-bart "List-initialization of an object or reference of type T is defined as follows: [...] — Otherwise, if T is an aggregate, aggregate initialization is performed." The clause is not *separate* from list-initialization. It's a special case thereof. – Barry Aug 21 '15 at 14:36