2014-11-28 10 views
11

Perché l'accesso a std::initializer_list non ci consente di modificare il suo contenuto? È un grande svantaggio di std::initializer_list quando lo si utilizza per lo scopo principale (per inizializzare un contenitore), dal momento che il suo utilizzo porta a un eccessivo lavoro di copia/costruzione copia, invece di costruzione-spostamento/assegnazione-spostamento.initializer_list natura immutabile porta a copia eccessiva

#include <initializer_list> 
#include <iostream> 
#include <vector> 

#include <cstdlib> 

struct A 
{ 

    A() = default; 
    A(A const &) { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
    A(A &&) { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
    A & operator = (A const &) { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; } 
    A & operator = (A &&) { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; } 

}; 

int 
main() 
{ 
    std::vector<A>{A{}, A{}, A{}}; 
    return EXIT_SUCCESS; 
} 

Output (come previsto):

A::A(const A &) 
A::A(const A &) 
A::A(const A &) 

Perché è il suo design così costretto?

+0

Come posso dedurre '{A {}, A {}, A {}}' costruisce tutti i contenuti della lista di inizializzazione in -posto. – Orient

+0

Inoltre è impossibile usare 'std :: initializer_list' per inizializzare i contenitori di oggetti non copiabili. – Orient

risposta

6

C'è un recente proposal for movable initializer lists, dove, in particolare, gli autori dicono:

std::initializer_list è stato progettato intorno al 2005 (N1890) al 2007 (N2215), prima di semantica mossa maturata, intorno 2009. Alla tempo, non era previsto che la copia semantica sarebbe insufficiente o addirittura subottimale per classi di valore comuni. C'era una proposta di 2008 N2801 Elenchi di inizializzatori e spostare la semantica ma C++ 0x si sentiva già scivolare in quel momento, e nel 2011 il caso era diventato freddo.

4

buona (se sfortunata) risposta di Anton.

Ecco il codice sorgente per l'esecuzione in libC++:

template <class _Tp, class _Allocator> 
inline _LIBCPP_INLINE_VISIBILITY 
vector<_Tp, _Allocator>::vector(initializer_list<value_type> __il) 
{ 
#if _LIBCPP_DEBUG_LEVEL >= 2 
    __get_db()->__insert_c(this); 
#endif 
    if (__il.size() > 0) 
    { 
     allocate(__il.size()); 
     __construct_at_end(__il.begin(), __il.end()); 
    } 
} 

non iteratori movimento in vista, quindi copia costruzione.

nel caso in cui è utile, ecco una soluzione alternativa utilizzando una lista di argomenti variadic:

#include <initializer_list> 
#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <iterator> 
#include <utility> 

#include <cstdlib> 

struct A 
{ 

    A() noexcept{ std::cout << __PRETTY_FUNCTION__ << std::endl; } 
    A(A const &) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
    A & operator = (A const &) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; } 
    A(A &&) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
    A & operator = (A &&) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; } 

}; 

template<class T, class...Args> 
void append_it(std::vector<T>& v) 
{ 
} 

template<class T, class...Args> 
void append_it(std::vector<T>& v, T&& t1, Args&&...args) 
{ 
    v.push_back(std::move(t1)); 
    append_it(v, std::forward<Args&&>(args)...); 
} 

template<class T, class...Args> 
std::vector<T> make_vector(T&& t1, Args&&...args) 
{ 
    std::vector<T> result; 
    result.reserve(1 + sizeof...(args)); 
    result.push_back(std::move(t1)); 
    append_it(result, std::forward<Args&&>(args)...); 
    return result; 
} 

int 
main() 
{ 
    auto v2 = make_vector(A{}, A{}, A{}); 

    return EXIT_SUCCESS; 
}