O padrão foi alterado desde que a pergunta (e a maioria das respostas) foi publicada na resolução deste relatório de defeitos .
A maneira de fazer um for(:)loop funcionar no seu tipo Xagora é uma das duas maneiras:
Criar membro X::begin()e X::end()retornar algo que age como um iterador
Crie uma função livre begin(X&)e end(X&)que retorne algo que age como um iterador, no mesmo espaço de nome que seu tipo X.
E semelhante para constvariações. Isso funcionará tanto nos compiladores que implementam as alterações no relatório de defeitos quanto nos compiladores que não o fazem.
Os objetos retornados não precisam ser realmente iteradores. O for(:)loop, diferente da maioria das partes do padrão C ++, é especificado para expandir para algo equivalente a :
for( range_declaration : range_expression )
torna-se:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
onde as variáveis que começam com __são apenas para exposição e begin_expre end_expré a mágica que chama begin/ end.²
Os requisitos do valor de retorno inicial / final são simples: você deve sobrecarregar pré- ++, garantir que as expressões de inicialização sejam válidas, binárias !=que podem ser usadas em um contexto booleano, unárias *que retornam algo com o qual você pode atribuir-inicializar range_declaratione expor um público destruidor.
Fazer isso de uma maneira que não seja compatível com um iterador é provavelmente uma péssima idéia, já que futuras iterações do C ++ podem ser relativamente descuidadas quanto à quebra de seu código, se você o fizer.
Como um aparte, é razoavelmente provável que uma revisão futura da norma permita end_exprretornar um tipo diferente begin_expr. Isso é útil, pois permite a avaliação de "final lento" (como a detecção de terminação nula) que é fácil de otimizar para ser tão eficiente quanto um loop C escrito à mão e outras vantagens semelhantes.
¹ Observe que os for(:)loops armazenam qualquer temporário em uma auto&&variável e o passam para você como um valor l. Você não pode detectar se está iterando sobre um valor temporário (ou outro rvalue); essa sobrecarga não será chamada por um for(:)loop. Veja [stmt.ranged] 1.2-1.3 da n4527.
² Ou chamar o begin/ endmétodo, ou ADL-única pesquisa de função livre begin/ end, ou mágica para suporte de matriz de estilo C. Observe que std::beginnão é chamado, a menos que range_expressionretorne um objeto do tipo namespace stdou dependente do mesmo.
No c ++ 17 a expressão de intervalo para foi atualizada
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
com os tipos de __begine __endforam dissociados.
Isso permite que o iterador final não seja do mesmo tipo que o inicial. O seu tipo de iterador final pode ser um "sentinela" que suporta apenas !=o tipo de iterador de início.
Um exemplo prático de por que isso é útil é que o iterador final pode ler "verifique seu char*para ver se aponta para '0'" quando estiver ==com a char*. Isso permite que uma expressão de intervalo de C ++ gere código ideal ao iterar em um char*buffer terminado por nulo .
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;
}
};
exemplo ao vivo em um compilador sem suporte completo a C ++ 17; forloop expandido manualmente.
begin/endou um amigo, estático ou gratuitobegin/end. Basta ter cuidado em que namespace você colocar a função livre: stackoverflow.com/questions/28242073/...