Mi sono ritrovato a dover identificare i tipi che soddisfano questa funzione molte volte. Non so se inventare un tale "concetto" speciale sia elegante (immagina che sia un concetto che riguarda la memoria, che non è molto astratto) ma sono d'accordo che qualcosa del genere sarà utile.
Nel frattempo per essere pratico e tradurre questo concetto/esigenza in un requisito di sintassi pura, cerchiamo di camminare all'indietro. Se ci limitiamo allo standard, quali sono le classi che garantiscono (o quasi garantiscono) contiguità? in ordine di importanza:
std::vector<T>
T[N] // !!
std::array<T, N>
std::string
std::initializer_list<T>
std::valarray<T>
Di tutti questi, std::vector
, std::array
, std::string
hanno una funzione membro chiamata .data()
. Quindi, se questo è sufficiente per te, puoi fare affidamento sulla presenza del membro .data() -> T*
per indicare la memoria contigua.
Sono disponibili due opzioni:
1) Fai la sforzo di utilizzare la funzione di membro .data()
di sollevare un errore di sintassi se il tipo non è contiguo. (Non difficile se si sostituisce ad esempio t[0]
da *t.data()
)
2) Utilizzare un tipo di SFINAE su .data()
.
template<class ContiguousSequence, typename = decltype(std::declval<ContigiousSequence>().data())>
void fun(ContiguousSequence&& s){...} // this function will only work with contiguous data
Inoltre, C++ 17 ha std::data
che generalizza a tutti i tipi con .data()
e sovraccarichi in aggiunta, per T[N]
e std::initializer_list<T>
. Quindi, è possibile sostituire ....data()
per std::data(...)
sopra.
La conclusione, ritengo sia una buona convenzione è che se un tipo ha una funzione data
(o .data()
in C++ 11) che restituisce un puntatore al tipo di valore allora gli elementi contigue.
(Ok, che dire std::valarray<T>
? Non funziona, a meno che non si sovraccarica std::data(std::valarray<T>&)
. Ma chi usa std::valarray
comunque? Si tratta di un caratteristico angolo abbandonato di C++, credo)
Infine, nota per esempio che ovviamente std::map
e meno ovviamente std::deque
non hanno una funzione .data()
(o std::data(...)
). boost::multi_array<..., N>
ha un membro .data()
e restituisce un puntatore all'elemento dell'array, non è chiaro se si tratta di una sequenza contigua nel senso desiderato (perché l'ordine non è evidente) ma in un certo senso è anche un'allocazione contigua della memoria.
EDIT: Ci sono due proposte che affrontano questo problema (ma a livello dei iteratori) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3884.pdfhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4284.html
'std :: array';) – Zeta
Per quanto ne so, non esiste un concetto del genere. 'std :: vector',' std :: string' e 'std :: array' hanno semplicemente l'invariante che' c.data() + i == & c [i] 'per' i' in '[0, c .size()) '. – Xeo
@Xeo: giusto. Non c'è alcun concetto, solo il requisito che gli elementi (o 'char_type's) siano *" memorizzati contigui "*. – Zeta