3

I have a (templated) function like this:

template<typename T>
void func(std::initializer_list<std::vector<T>> args)
{
    for (const auto &values : inputValues)
    {
        someOperation(values);
    }
}

But I'm pretty sure this is not what I want. What I want is a function that can receive an arbitrary number of std:vector<T>s avoiding copies (in fact, they should be received as const since I am not modifying them inside). Ideally, I would like to be able to pass both rvalues and lvalues, like:

std::vector<int> myVec {1, 2, 3};
func({myVec, {4, 5}});

The problem that I have with the current declaration is that I think (although I have not found any source explicitly confirming or refuting this) that, as it is right now, the initializer list will copy the vectors on creation (I know that they would not get copied when the initialization list itself is copied in any case); is this correct? If that is the case, is:

template<typename T>
void func(std::initializer_list<const & std::vector<T>> args)
{
    // ...
}

What I am looking for? And would that allow me to use rvalues? I am open to options different than initializer lists (I'm not sure I'd be able to use variadic templates, because this function template is explicitly instantiated and compiled into a library, but if there is an alternative with that it would be interesting to see).

jdehesa
  • 58,456
  • 7
  • 77
  • 121

1 Answers1

4

You do not want an initializer_list because it will force a copy if you attempt to initialize other vectors with it. Instead you want forwarding references with a variadic template:

template<class... T>
bool func(T&&... vectors);

Which you can then std::forward<T>(vectors)... as you see fit.

This allows you to opportunistically avoid copies.

For example

std::vector<int> a;
std::vector<int> b;
std::vector<int> c;
// initialize a, b, and c

func(std::move(a), std::move(b), std::move(c));

If you want to enforce all the vectors are received by const ref, try this:

template<class... Vec>
bool func(const Vec&... vectors);

//...
func(a, b, c); // pass all by const ref

An initializer_list is a bad choice in many scenarios not only because of the copy-on-construct issue, but also because it refers to an array of elements with local scope. The implication here is that if you ever wanted to copy return it, you'd end up with silent undefined behavior (and if you accidentally disable RVO, or the compiler doesn't implement RVO or implements it improperly for initializer_list, you're out of luck again)


Re your edit:

I am not actually using the initializer list to initialize anything, I just use it as a lightweight arguments container and, iterating its values and read the values in the vectors (I'll update the question to make that clearer). Would this also cause the creation of copies?

No it wouldn't necessitate any copy creation. There's no problem with using an initializer_list that way per se, but that's not its intended use. It's intended use is for a class constructor to have an initializer_list constructor. If you're looking for a container you can iterate over, there's no harm in making a vector of references to your other vectors (std::vector<const std::vector<int>&>)

AndyG
  • 39,700
  • 8
  • 109
  • 143
  • @PasserBy: I guess what I'm saying is that if you want to initialize some object via an initializer_list, then it will copy those items; there's no option to move. You're right, however, that copying the initializer_list itself will not copy the items (and also that you should avoid copy returning an initializer list because of the fact that the items have local scope) – AndyG Jul 14 '17 at 11:48
  • Thanks, those are some good options and insights in general. However, I am not actually using the initializer list to initialize anything, I just use it as a lightweight arguments container and, iterating its values and read the values in the vectors (I'll update the question to make that clearer). Would this also cause the creation of copies? – jdehesa Jul 14 '17 at 12:41
  • 1
    @jdehesa: No it wouldn't necessitate any copy creation. There's no problem with using an `initializer_list` that way per se, but that's not its intended use. It's intended use is for a class constructor to have an initializer_list constructor. If you're looking for a container you can iterate over, there's no harm in making a vector of references to your other vectors (`std::vector&>`) – AndyG Jul 14 '17 at 13:18
  • Okay, that comment along with the rest of the answer solves my question, I think I'll do as you suggest. Thanks! – jdehesa Jul 14 '17 at 13:20