Uno dei vantaggi std::begin
e std::end
è che servono come punti di estensione per l'implementazione dell'interfaccia standard per le classi esterne.
Se desideri utilizzare la CustomContainer
classe con intervallo per la funzione loop o template che prevede .begin()
e .end()
metodi, dovresti ovviamente implementare quei metodi.
Se la classe fornisce questi metodi, non è un problema. Altrimenti, dovresti modificarlo *.
Ciò non è sempre possibile, ad esempio quando si utilizza una libreria esterna, specialmente commerciale e di tipo chiuso.
In tali situazioni, std::begin
e std::end
tornare utile, dal momento che è possibile fornire API iteratore senza modificare la classe stessa, ma piuttosto sovraccaricare le funzioni gratuite.
Esempio: supponiamo che desideri implementare la count_if
funzione che accetta un contenitore anziché una coppia di iteratori. Tale codice potrebbe apparire così:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
Ora, per qualsiasi classe che desideri utilizzare con questa personalizzazione count_if
, devi solo aggiungere due funzioni gratuite, invece di modificare quelle classi.
Ora, C ++ ha un meccanismo chiamato Argument Dependent Lookup
(ADL), che rende tale approccio ancora più flessibile.
In breve, ADL significa che quando un compilatore risolve una funzione non qualificata (ovvero una funzione senza spazio dei nomi, come al begin
posto di std::begin
), prenderà in considerazione anche le funzioni dichiarate negli spazi dei nomi dei suoi argomenti. Per esempio:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
In questo caso, non importa quali siano i nomi qualificati some_lib::begin
e some_lib::end
, poiché lo CustomContainer
è some_lib::
anche, il compilatore utilizzerà tali sovraccarichicount_if
.
Questo è anche il motivo per avere using std::begin;
e using std::end;
entrare count_if
. Questo ci consente di utilizzare non qualificati begin
e end
, quindi, consentire ADL e
consentire al compilatore di scegliere std::begin
e std::end
quando non vengono trovate altre alternative.
Possiamo mangiare il cookie e avere il cookie, ovvero avere un modo per fornire l'implementazione personalizzata di begin
/ end
mentre il compilatore può tornare a quelli standard.
Alcune note:
Per lo stesso motivo, ci sono altre funzioni simili: std::rbegin
/ rend
,
std::size
e std::data
.
Come menzionano altre risposte, le std::
versioni presentano sovraccarichi per array nudi. È utile, ma è semplicemente un caso speciale di quello che ho descritto sopra.
Usare std::begin
e gli amici è una buona idea quando si scrive un codice modello, perché questo rende questi modelli più generici. Per i non-template potresti anche usare i metodi, quando applicabile.
PS Sono consapevole che questo post ha quasi 7 anni. Mi sono imbattuto perché volevo rispondere a una domanda contrassegnata come duplicata e ho scoperto che nessuna risposta menziona ADL.