Search code examples
c++arraysc++11initializer-list

array vs std::initializer_list as function parameter


I can write a function which take a temporary array(such as {1, 2, 3}) as an argument in two ways:

// using array
template<typename T, int N>
auto foo1(const T(&t)[N]) -> void;

// using std::initializer_list
template<typename T>
auto foo2(std::initializer_list<T> t) -> void;

Is there any guideline which tells which one is better?


Solution

  • They are both very different things. There is also 2 or 3 other choices that are reasonable.

    template<class T, std::size_t N>
    void foo_a( std::array<T, N> const& );
    
    template<class T>
    void foo_b( gsl::span<const T> );
    
    template<class T, std::size_t N >
    void foo_c( T const(&)[N] );
    
    template<class T>
    void foo_d( std::initializer_list<T> );
    
    template<class T, class A=std::allocator<T> >
    void foo_e( std::vector<T, A> const& );
    
    template<class...Ts>
    void foo_f( std::tuple<Ts...> const& );
    
    template<class...Ts>
    void foo_g( Ts const& ... );
    

    here are 7 different ways to take a bunch of Ts.

    They all have advantages and disadvantages over each other.

    The closest to strictly better is foo_a over foo_c; foo_c is only good in that it is more compatible with C-style arrays.

    foo_b lets you consume any of the others except foo_f. So that is nice.

    a, c and f all have compile-time determined lengths within foo. This could make a difference, depending on what you are doing. You could in theory write a foo_b type view that handles fixed length, but nobody bothers.

    e is the only one that supports dynamic length at the call-site.

    f supports non-identical types, but makes iteration a bit less clean.

    All of them can be modified slightly to permit move-out (even the initializer list one with a bit more boilerplate).

    d gives the easiest {}, but g is just as clean (omit the {} entirely).

    Usually I use my home-rolled gsl::span variant. It has an initializer_list constructor. And I very very rarely want to deduce T.