A comparação de iteradores begin()
e de dois s que apontam para a mesma memória está bem definida?end()
std::span
#include <span>
#include <cassert>
int main() {
int arr[5]{1,2,3,4,5};
std::span s1{arr};
std::span s2{arr};
assert(s1.begin() == s2.begin());
assert(s1.end() == s2.end());
assert(s1.begin() + s1.size() == s2.end());
}
Todas as afirmações passam em todas as implementações std::span
até agora, mas há algo que eu possa estar esquecendo que torna isso um bug, por exemplo, UB?
Para contextualizar, isso pode ocorrer se você tiver uma classe que tenta esconder seus componentes internos com um span.
class some_class
{
public:
std::span<int> vec_view() { return vec; }
private:
std::vector<int> vec;
};
int main() {
some_class c;
std::for_each(c.vec_view().begin(), c.vec_view().end(), [](auto&){});
}
Isso está relacionado a C++ permite comparação entre std::span::iterators quando um span é um subspan do outro?, mas esta questão não é sobre um std::subspan
, e além disso a std::span
versão também passa asserções MSVC, diferente da versão com o std::subspan
.
Quem sabe.
std::span::iterator
modeloscontiguous_iterator
( [span.iterators] ), e portanto,forward_iterator
. [iterator.concept.forward] parágrafo 2 se aplica:O padrão C++ nunca define o que é uma "sequência subjacente".
.begin()
e.end()
continuamos, entãostd::span(...).begin() == std::span(...).begin()
ela é indefinida, não importa o que aconteça.operator*
nos iteradores, entãos1.begin() == s2.begin()
tem que sertrue
.A primeira opção é desnecessariamente restritiva para intervalos não proprietários como
std::string_view
estd::span
. A segunda opção pode ter consequências indesejadas, como a possibilidade de comparar iteradores destd::ranges::filter_view
s completamente diferentes criados com um ponteiro de função como predicado (mesmo tipo de iterador, mesmos elementos, mas a comparação não faz sentido). Algumas decisões de design da linguagem precisarão ser tomadas aqui, possivelmente caso a caso.Como você já apontou, há um problema ativo do LWG, o problema 3989 , e até que ele seja resolvido, não haverá uma resposta definitiva para essa questão, mesmo para dois
std::span
s que são equivalentes, não apenas subspans.Não acho que isso seja um comportamento indefinido. Afinal,
std::span
é apenas uma classe, não faz parte do núcleo da linguagem, então acho que isso deveria ser apenas um comportamento não especificado .Um span::iterator não tem permissão para conter uma referência ao intervalo de origem, portanto, além de um ponteiro para o elemento referenciado, ele só pode conter duas informações adicionais: span.data() e span.size().
Portanto, enquanto
span1.data() == span2.data() && span1.size() == span2.size()
, é garantido que iteradores de diferentes intervalos podem ser comparados.