why is `std::initializer_list` often passed by value?

In almost every post I see on SO, involving a std::initializer_list, people tend to pass a std::initializer_list by value. According to this article:

https://web.archive.org/web/20120707045924/cpp-next.com/archive/2009/08/want-speed-pass-by-value/

one should pass by value, if one wants to make a copy of the passed object. But copying a std::initializer_list is not a good idea, as

Copying a std::initializer_list does not copy the underlying objects. The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended.

So why is an instance of it often passed by value and not by, say const&, which guaranteed does not make a needless copy?

13214 次浏览

It’s passed by value because it’s cheap. std::initializer_list, being a thin wrapper, is most likely implemented as a pair of pointers, so copying is (almost) as cheap as passing by reference. In addition, we’re not actually performing a copy, we’re (usually) performing a move since in most cases the argument is constructed from a temporary anyway. However, this won’t make a difference for performance – moving two pointers is as expensive as copying them.

On the other hand, accessing the elements of a copy may be faster since we avoid one additional dereferencing (that of the reference).

Probably for the same reasons iterators are almost always passed by value: copying an iterator is considered "cheap". In the case of initializer_list, there's also the fact that most instances will be temporaries with trivial destructors, so the compiler can construct them directly where it puts the function argument, with no copy. Finally, there's the fact that, like iterators, the called function is likely to want to modify the value, which means that it would have to copy it locally if it were passed by a reference to const.

EDIT:

Just to generalize: the standard library makes the assumption in general that it is better to pass iterators, initializer_lists, and functional objects by value. As a result, you should ensure that any iterators, iterator_lists or functional objects you design are cheap to copy, and you should assume in your own code that they are cheap to copy. The traditional rule about when to use references to const, and when to use value, should probably be modified to reflect this:

Use pass by reference to const for class types other than iterators, initializer_lists or functional objects; use pass by value otherwise.