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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.