Qual é o objetivo do C ++ 20 std :: common_reference?

Respostas:

46

common_reference saiu dos meus esforços para apresentar uma conceituação dos iteradores da STL que acomoda iteradores de proxy.

No STL, os iteradores têm dois tipos associados de interesse particular: referencee value_type. O primeiro é o tipo de retorno do iterador operator*e o value_typeé o tipo (não const, não referência) dos elementos da sequência.

Algoritmos genéricos geralmente precisam fazer coisas como esta:

value_type tmp = *it;

... então sabemos que deve haver alguma relação entre esses dois tipos. Para iteradores não proxy, o relacionamento é simples: referenceé sempre value_type, opcionalmente, const e referência qualificado. As primeiras tentativas de definir o InputIteratorconceito exigiram que a expressão *itfosse conversível e const value_type &, para a maioria dos iteradores interessantes, é suficiente.

Eu queria que os iteradores no C ++ 20 fossem mais poderosos que isso. Por exemplo, considere as necessidades de um zip_iteratorque itere duas sequências na etapa de bloqueio. Ao zip_iteratorremover a referência a , você obtém temporariamente pairos referencetipos dos dois iteradores . Portanto, ziping a vector<int>e a vector<double>teriam os seguintes tipos associados:

zipiterador reference: pair<int &, double &>
zipiterador value_type:pair<int, double>

Como você pode ver, esses dois tipos não estão relacionados entre si simplesmente adicionando a qualificação cv e ref de nível superior. E, no entanto, deixar os dois tipos serem arbitrariamente diferentes parece errado. Claramente, há algum relacionamento aqui. Mas qual é o relacionamento e o que os algoritmos genéricos que operam nos iteradores assumem com segurança sobre os dois tipos?

A resposta no C ++ 20 é que, para qualquer tipo de iterador válido, proxy ou não, os tipos reference &&e value_type &compartilham uma referência comum . Em outras palavras, para alguns iteradores, itexiste algum tipo CRque torna o seguinte bem formado:

void foo(CR) // CR is the common reference for iterator I
{}

void algo( I it, iter_value_t<I> val )
{
  foo(val); // OK, lvalue to value_type convertible to CR
  foo(*it); // OK, reference convertible to CR
}

CRé a referência comum. Todos os algoritmos podem confiar no fato de que esse tipo existe e podem ser usados std::common_referencepara computá-lo.

Então, esse é o papel que common_referencedesempenha no STL no C ++ 20. Geralmente, a menos que você esteja escrevendo algoritmos genéricos ou iteradores de proxy, você pode ignorá-lo com segurança. Está lá embaixo, garantindo que seus iteradores cumpram suas obrigações contratuais.


EDIT: O PO também pediu um exemplo. Isso é um pouco artificial, mas imagine que seja C ++ 20 e você receba um intervalo rde acesso aleatório do tipo Rsobre o qual você não sabe nada e deseja sorto intervalo.

Imagine ainda que, por algum motivo, você deseja usar uma função de comparação monomórfica, como std::less<T>. (Talvez você tenha apagado o intervalo de digitação e também precise apagar a função de comparação e passá-la através de um virtual? Novamente, um trecho.) O que deve Testar std::less<T>? Para isso você usaria common_reference, ou o ajudante iter_common_reference_tque é implementado em termos dele.

using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});

Isso garante que funcione, mesmo se o intervalo rtiver iteradores de proxy.

Eric Niebler
fonte
2
Talvez eu seja tenso, mas você pode esclarecer qual é a referência comum no exemplo do zip-pair?
happydave
4
Idealmente, pair<T&,U&>e pair<T,U>&teria uma referência comum, e seria simplesmente pair<T&,U&>. No entanto, para std::pair, não há conversão de pair<T,U>&para, pair<T&,U&>mesmo que essa conversão seja sólida em princípio. (Esta, aliás, é por isso que não temos uma zipvista em C ++ 20.)
Eric Niebler
4
@EricNiebler: " É por isso que não temos uma visualização zip no C ++ 20. " Existe alguma razão pela qual um iterador zip precisaria usar pair, em vez de um tipo que poderia ser projetado especificamente para esse fim , com conversões implícitas apropriadas, conforme necessário?
Nicol Bolas
5
@Nicol Bolas Não há necessidade de usar std::pair; qualquer tipo adequado de par com as conversões apropriadas servirá, e o range-v3 define esse tipo de par. No Comitê, o LEWG não gostou da idéia de adicionar à Biblioteca Padrão um tipo que fosse quase, mas não tanto std::pair, normativo ou não, sem antes fazer a devida diligência sobre os prós e contras de simplesmente fazer o std::pairtrabalho.
Eric Niebler
3
tuple, pair, tomato, to- MAH- to. pairpossui esse recurso interessante que você pode acessar os elementos com .firste .second. Ligações estruturadas ajudam com alguns dos constrangimentos de trabalhar com tuples, mas não com todos.
Eric Niebler