Exibindo um ponteiro bruto como um intervalo no loop for baseado em intervalo
Como faço para que um ponteiro bruto se comporte como um intervalo, para uma sintaxe de loop para um intervalo.
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;// will not execute if the pointer is null
Motivação:
Agora é vox populi que umboost::optional
(futurostd::optional
) pode ser visto como um intervalo e, portanto, usado em um loop for rangehttp://faithandbrave.hateblo.jp/entry/2015/01/29/173613.
Quando reescrevi minha própria versão simplificada:
namespace boost {
template <class Optional>
decltype(auto) begin(Optional& opt) noexcept{
return opt?&*opt:nullptr;
}
template <class Optional>
decltype(auto) end(Optional& opt) noexcept{
return opt?std::next(&*opt):nullptr;
}
}
Usado como
boost::optional<int> opt = 3;
for (int& x : opt) std::cout << x << std::endl;
Enquanto procurava esse código, imaginei que poderia ser generalizado para ponteiros brutos (anuláveis) também.
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;
em vez do habitualif(dptr) std::cout << *dptr << std::endl;
. O que é bom, mas eu queria alcançar a outra sintaxe acima.
Tentativas
Primeiro eu tentei fazer o acimaOptional
versão debegin
eend
trabalhar para ponteiros, mas não pude. Decidi ser explícito nos tipos e remover todos os modelos:
namespace std{ // excuse me, this for experimenting only, the namespace can be removed but the effect is the same.
double* begin(double* opt){
return opt?&*opt:nullptr;
}
double* end(double* opt){
return opt?std::next(&*opt):nullptr;
}
}
Quase lá, funciona para
for(double* ptr = std::begin(dptr); ptr != std::end(dptr); ++ptr)
std::cout << *ptr << std::endl;
Mas isso não funciona para osupostamente equivalente loop for-range:
for(double& d : dptr) std::cout << d << std::endl;
Dois compiladores me dizem:error: invalid range expression of type 'double *'; no viable 'begin' function available
O que está acontecendo? Existe uma mágica de compilador que proíbe que o loop à distância funcione para ponteiros. Estou fazendo uma suposição errada sobre a sintaxe do loop à distância?
Ironicamente, no padrão há uma sobrecarga parastd::begin(T(&arr)[N])
e isso é muito próximo disso.
Nota e um segundo embora
Sim, a ideia é boba porque, mesmo se possível, isso seria muito confuso:
double* ptr = new double[10];
for(double& d : ptr){...}
iteraria apenas o primeiro elemento. Uma solução alternativa mais clara e realista seria fazer algo como a solução proposta por @Yakk:
for(double& d : boost::make_optional_ref(ptr)){...}
Dessa maneira, fica claro que estamos iterando apenas um elemento e que esse elemento é opcional.
Ok, ok, eu vou voltar paraif(ptr) ... use *ptr
.