Il existe plusieurs approches, chacun avec leurs propres avantages et inconvénients. Ci-dessous trois approches, avec une analyse coûts-avantages.
ADL par le biais de la coutume non-membre de l' begin()
/ end()
La première variante de l'offre non-membre de l' begin()
et end()
les modèles de fonction à l'intérieur d'un legacy
de l'espace de noms de modernisation de la fonctionnalité requise sur une catégorie ou d'une classe template qui peut le fournir, mais a par exemple un mauvais conventions de nommage. Code appelant peut alors compter sur l'ADL de trouver ces nouvelles fonctions. Exemple de code (sur la base des observations par @Xeo):
// LegacyContainerBeginEnd.h
namespace legacy {
// retro-fitting begin() / end() interface on legacy
// Container class template with incompatible names
template<class C>
auto begin(Container& c) -> decltype(c.legacy_begin())
{
return c.legacy_begin();
}
// similarly for begin() taking const&, cbegin(), end(), cend(), etc.
} // namespace legacy
// print.h
template<class C>
void print(C const& c)
{
// bring into scope to fall back on for types without their own namespace non-member begin()/end()
using std::begin;
using std::end;
// works for Standard Containers, C-style arrays and legacy Containers
std::copy(begin(c), end(c), std::ostream_iterator<decltype(*begin(c))>(std::cout, " ")); std::cout << "\n";
// alternative: also works for Standard Containers, C-style arrays and legacy Containers
for (auto elem: c) std::cout << elem << " "; std::cout << "\n";
}
Pros: cohérence et laconique convention d'appel qui fonctionne complètement générique
- fonctionne pour n'importe quel Conteneur Standard et de l'utilisateur-types qui définissent membre
.begin()
et .end()
- oeuvres de style C tableaux
- peut être monté au travail (également pour les de gamme pour les boucles!) pour tout modèle de classe
legacy::Container<T>
qui n'ont pas membre de l' .begin()
et end()
sans nécessiter de modifications de code source
Inconvénients: nécessite l'utilisation de déclarations dans de nombreux endroits
-
std::begin
et std::end
sont nécessaires à ont été introduites dans chaque explicite l'appel de la portée de " retour d'options pour le style C tableaux (écueil potentiel pour modèle des en-têtes et de nuisance générale)
ADL par le biais de la coutume non-membre de l' adl_begin()
et adl_end()
Une deuxième solution consiste à encapsuler les à l'aide de déclarations de la solution précédente dans un distinct adl
de l'espace de noms par non-membre, les modèles de fonction adl_begin()
et adl_end()
, qui peut également être trouvé par le biais de l'ADL. Exemple de code (sur la base des observations par @Yakk):
// LegacyContainerBeginEnd.h
// as before...
// ADLBeginEnd.h
namespace adl {
using std::begin; // <-- here, because otherwise decltype() will not find it
template<class C>
auto adl_begin(C && c) -> decltype(begin(std::forward<C>(c)))
{
// using std::begin; // in C++14 this might work because decltype() is no longer needed
return begin(std::forward<C>(c)); // try to find non-member, fall back on std::
}
// similary for cbegin(), end(), cend(), etc.
} // namespace adl
using adl::adl_begin; // will be visible in any compilation unit that includes this header
// print.h
# include "ADLBeginEnd.h" // brings adl_begin() and adl_end() into scope
template<class C>
void print(C const& c)
{
// works for Standard Containers, C-style arrays and legacy Containers
std::copy(adl_begin(c), adl_end(c), std::ostream_iterator<decltype(*adl_begin(c))>(std::cout, " ")); std::cout << "\n";
// alternative: also works for Standard Containers, C-style arrays and legacy Containers
// does not need adl_begin() / adl_end(), but continues to work
for (auto elem: c) std::cout << elem << " "; std::cout << "\n";
}
Pros: la constance de la convention d'appel qui fonctionne complètement générique
- même les pros comme pour @Xeo la suggestion de +
- le répétées à l'aide de déclarations ont été encapsulés (SEC)
Inconvénients: un peu verbeux
-
adl_begin()
/ adl_end()
n'est pas aussi laconique qu' begin()
/ end()
- c'est peut-être pas aussi idiomatiques (même si c'est explicite)
- dans l'attente de C++14 type de retour de la déduction, sera également de polluer l'espace de noms avec
std::begin
/ std::end
REMARQUE: vous ne savez Pas si c'est vraiment améliore l'approche précédente.
Explicitement de qualification std::begin()
ou std::end()
partout
Une fois que le niveau de verbosité des begin()
/ end()
a été donné de toute façon, pourquoi ne pas revenir à la qualification des appels d' std::begin()
/ std::end()
? Exemple de code:
// LegacyIntContainerBeginEnd.h
namespace std {
// retro-fitting begin() / end() interface on legacy IntContainer class
// with incompatible names
template<>
auto begin(legacy::IntContainer& c) -> decltype(c.legacy_begin())
{
return c.legacy_begin();
}
// similary for begin() taking const&, cbegin(), end(), cend(), etc.
} // namespace std
// LegacyContainer.h
namespace legacy {
template<class T>
class Container
{
public:
// YES, DOCUMENT REALLY WELL THAT THE EXISTING CODE IS BEING MODIFIED
auto begin() -> decltype(legacy_begin()) { return legacy_begin(); }
auto end() -> decltype(legacy_end()) { return legacy_end(); }
// rest of existing interface
};
} // namespace legacy
// print.h
template<class C>
void print(C const& c)
{
// works for Standard Containers, C-style arrays as well as
// legacy::IntContainer and legacy::Container<T>
std::copy(std::begin(c), std::end(c), std::ostream_iterator<decltype(*std::begin(c))>(std::cout, " ")); std::cout << "\n";
// alternative: also works for Standard Containers, C-style arrays and
// legacy::IntContainer and legacy::Container<T>
for (auto elem: c) std::cout << elem << " "; std::cout << "\n";
}
Pros: la constance de la convention d'appel qui fonctionne presque de façon générique
- fonctionne pour n'importe quel Conteneur Standard et de l'utilisateur-types qui définissent membre
.begin()
et .end()
- oeuvres de style C tableaux
Inconvénients: un peu verbeux et de la modernisation n'est pas générique et un problème de maintenance
-
std::begin()
/ std::end()
est un peu plus prolixe que d' begin()
/ end()
- ne peut être monté en rattrapage de travail (également pour les de gamme pour les boucles!) pour toute la classe
LegacyContainer
qui n'ont pas membre de l' .begin()
et end()
(et pour lequel il n'y a pas de code source!) en fournissant explicite spécialisations de la non-membre, les modèles de fonction begin()
et end()
en namespace std
- ne peut être monté sur des modèles de classe
LegacyContainer<T>
en ajoutant directement des fonctions de membre begin()
/ end()
à l'intérieur du code source de l' LegacyContainer<T>
(pour les modèles est disponible). L' namespace std
astuce ne fonctionne pas ici, parce que les modèles de fonction ne peuvent pas être partiellement spécialisées.
Ce qu'il faut utiliser?
L'ADL approche par le biais de la non-membre de l' begin()
/ end()
dans un récipient propre espace de noms est la idiomatiques C++11 approche, en particulier pour les fonctions génériques qui nécessitent un équipement ultérieur de l'héritage des classes et des modèles de classe. C'est le même langage que pour l'utilisateur-non-membre de l' swap()
fonctions.
Pour le code qui utilise uniquement des Conteneurs Standard ou de type C tableaux, std::begin()
et std::end()
pourrait être appelé partout sans introduire à l'aide de déclarations, au détriment des plus détaillé des appels. Cette approche peut encore être rénovées, mais il nécessite de jongler avec namespace std
(pour les types de classe) ou le lieu de la source de modifications (pour les modèles de classe). Il peut être fait, mais c'est pas la peine de la gestion de la difficulté.
Dans le code non générique, où le conteneur en question est connue au codage en temps, on pourrait même compter sur l'ADL pour les Conteneurs Standard uniquement et explicitement qualifier std::begin
/ std::end
C-le style des tableaux. Il perd un peu l'appel de la cohérence, mais enregistre sur l'utilisation de déclarations.