Reading the answer to this question, I was surprised to find that std::min(std::initializer_list<T>)
takes its arguments by value.
If you use std::initializer_list
in the way implied by its name, i.e. as an initializer to some object, I understand that we don't care about copying its elements since they will be copied anyway to initialize the object. However, in this case here we most likely don't need any copy, so it would seem much more reasonable to take the arguments as std::initializer_list<const T&>
if only it were possible.
What's the best practice for this situation? Should you not call the initializer_list
version of std::min
if you care about not making unnecessary copies, or is there some other trick to avoid the copy?
There is no such thing as std::initializer_list<const T&>
. Initializer lists can only hold objects by value.
See [dcl.init.list]/5:
An object of type
std::initializer_list<E>
is constructed from an initializer list as if the implementation allocated a temporary array of N elements of typeconst E
, where N is the number of elements in the initializer list.
There are no arrays of references, so there cannot be an initializer_list
of references either.
My suggestion in this circumstance would be to write a replacement for std::min
which takes a variadic template of forwarding references. This works (although I'm sure it can be improved):
template<typename T, typename... Args>
T vmin( T arg1, Args&&... args )
{
T *p[] = { &arg1, &args... };
return **std::min_element( begin(p), end(p),
[](T *a, T *b) { return *a < *b; } );
}
See it working - using min
or mmin
, two extra copies are made (for a
and b
).
To sum up the comments:
An std::initializer_list
is supposed to be a lightweight proxy as described on cppreference.com.
Therefore a copy of an initializer list should be very fast since the underlying elements are not copied:
C++11 §18.9/2
An object of type
initializer_list<E>
provides access to an array of objects of typeconst E.
[ Note: A pair of pointers or a pointer plus a length would be obvious representations forinitializer_list
.initializer_list
is used to implement initializer lists as specified in 8.5.4. Copying an initializer list does not copy the underlying elements. — end note ]
Using a reference though would boil down to using a pointer and therefore an additional indirection.
The actual problem of std::min
therefore is not that it takes the initializer_list
by value, but rather, that the arguments to initializer_list
have to be copied if they are computed at runtime. [1]
That the benchmark at [1] was broken as found out later is unfortunate.
auto min_var = std::min({1, 2, 3, 4, 5}); // fast
auto vec = std::vector<int>{1, 2, 3, 4, 5};
min_var = std::min({vec[0], vec[1], vec[2], vec[3], vec[4]}); // slow
[1]: N2722 p. 2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With