La norme a été modifiée depuis que la question (et la plupart des réponses) ont été publiées dans la résolution de ce rapport d'anomalie .
La façon de faire fonctionner une for(:)boucle sur votre type Xest désormais l'une des deux façons suivantes:
Créer un membre X::begin()et X::end()renvoyer quelque chose qui agit comme un itérateur
 
Créez une fonction gratuite begin(X&)et end(X&)qui retourne quelque chose qui agit comme un itérateur, dans le même espace de nom que votre type X.¹
 
Et similaire pour les constvariations. Cela fonctionnera à la fois sur les compilateurs qui implémentent les modifications de rapport de défaut et sur les compilateurs qui ne le font pas.
Les objets retournés ne doivent pas nécessairement être des itérateurs. La for(:)boucle, contrairement à la plupart des parties de la norme C ++, est spécifiée pour s'étendre à quelque chose d'équivalent à :
for( range_declaration : range_expression )
devient:
{
  auto && __range = range_expression ;
  for (auto __begin = begin_expr,
            __end = end_expr;
            __begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
  }
}
où les variables commençant par __sont uniquement destinées à l'exposition, et begin_expret end_exprest la magie qui appelle begin/ end.²
Les exigences sur la valeur de retour de début / fin sont simples: vous devez surcharger la pré- ++, vous assurer que les expressions d'initialisation sont valides, binaires !=qui peuvent être utilisés dans un contexte booléen, unaire *qui renvoie quelque chose que vous pouvez assigner-initialiserrange_declaration et exposer un public destructeur.
Le faire d'une manière qui n'est pas compatible avec un itérateur est probablement une mauvaise idée, car les futures itérations de C ++ pourraient être relativement cavalières sur la rupture de votre code si vous le faites.
Soit dit en passant, il est raisonnablement probable qu'une future révision de la norme permettra end_exprde renvoyer un type différent de celui begin_expr. Ceci est utile car il permet une évaluation "paresseuse" (comme la détection de terminaison nulle) qui est facile à optimiser pour être aussi efficace qu'une boucle C manuscrite, et d'autres avantages similaires.
¹ Notez que les for(:)boucles stockent tout temporaire dans une auto&&variable et vous le transmettent en tant que valeur l. Vous ne pouvez pas détecter si vous parcourez une valeur temporaire (ou une autre valeur); une telle surcharge ne sera pas appelée par unfor(:) boucle. Voir [stmt.ranged] 1.2-1.3 de n4527.
² Appelez la méthode begin/ end, ou la recherche ADL uniquement de la fonction gratuite begin/ end, ou magic pour la prise en charge des tableaux de style C. Notez qu'il std::beginn'est pas appelé sauf s'il range_expressionrenvoie un objet de type dans namespace stdou dépendant de celui-ci.
Dans c ++ 17 l'expression range-for a été mise à jour
{
  auto && __range = range_expression ;
  auto __begin = begin_expr;
  auto __end = end_expr;
  for (;__begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
  }
}
avec les types de __beginet __endont été découplés.
Cela permet à l'itérateur final de ne pas être du même type que begin. Votre type d'itérateur de fin peut être une "sentinelle" qui ne prend !=en charge qu'avec le type d'itérateur de début.
Un exemple pratique de la raison pour laquelle cela est utile est que votre itérateur final peut lire "vérifiez votre char*pour voir s'il pointe vers '0'" lorsqu'il est ==avec un char*. Cela permet à une expression d'intervalle C ++ de générer du code optimal lors de l'itération sur un char*tampon terminé par null .
struct null_sentinal_t {
  template<class Rhs,
    std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
  >
  friend bool operator==(Rhs const& ptr, null_sentinal_t) {
    return !*ptr;
  }
  template<class Rhs,
    std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
  >
  friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
    return !(ptr==null_sentinal_t{});
  }
  template<class Lhs,
    std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
  >
  friend bool operator==(null_sentinal_t, Lhs const& ptr) {
    return !*ptr;
  }
  template<class Lhs,
    std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
  >
  friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
    return !(null_sentinal_t{}==ptr);
  }
  friend bool operator==(null_sentinal_t, null_sentinal_t) {
    return true;
  }
  friend bool operator!=(null_sentinal_t, null_sentinal_t) {
    return false;
  }
};
exemple en direct dans un compilateur sans prise en charge complète de C ++ 17; forboucle développée manuellement.
               
              
begin/endou un ami, statique ou librebegin/end.