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

Solution 1:

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).

Solution 2:

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.