Uno dei vantaggi std::begine std::endè che servono come punti di estensione per l'implementazione dell'interfaccia standard per le classi esterne.
Se desideri utilizzare la CustomContainerclasse 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::begine std::endtornare 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_iffunzione 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 beginposto 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::begine 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 begine end, quindi, consentire ADL e
consentire al compilatore di scegliere std::begine std::endquando non vengono trovate altre alternative.
Possiamo mangiare il cookie e avere il cookie, ovvero avere un modo per fornire l'implementazione personalizzata di begin/ endmentre il compilatore può tornare a quelli standard.
Alcune note:
Per lo stesso motivo, ci sono altre funzioni simili: std::rbegin/ rend,
std::sizee 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::begine 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.