Como entender a resolução proposta do # 1664

8

Depois de examinar a resolução proposta no # 1664 ( resolução proposta no 1664 ), estou confuso com as regras de um argumento padrão de um modelo de função, cite o conteúdo aqui:

De acordo com 8.1.5 [expr.prim.lambda] parágrafo 3

O tipo de fechamento é declarado no menor escopo de bloco, escopo de classe ou espaço de nome que contém a expressão lambda correspondente. [Nota: isso determina o conjunto de espaços para nome e classes associados ao tipo de fechamento (6.4.2 [basic.lookup.argdep]). Os tipos de parâmetro de um declarador lambda não afetam esses espaços de nomes e classes associados. - end note]

No entanto, o parágrafo 13.8.1 [temp.inst] 13 diz

Se um modelo de função f for chamado de uma maneira que exija a utilização de um argumento padrão, os nomes dependentes serão consultados, as restrições semânticas serão verificadas e a instanciação de qualquer modelo usado no argumento padrão será feita como se o argumento padrão havia sido um inicializador usado em uma especialização de modelo de função com o mesmo escopo, os mesmos parâmetros de modelo e o mesmo acesso que o modelo de função f usado naquele momento.

Uma possibilidade, então, é que o tipo de fechamento da expressão lambda em um argumento padrão para uma função de modelo (ou, presumivelmente, uma função de membro de um modelo de classe) seja considerado como declarado em algum escopo de bloco no corpo de a especialização do modelo de função fictícia.

Considere o seguinte exemplo:

 namespace J {
    inline namespace K {
      template <typename T> int zap(const T &t) { foo(t); return 0; }
      template <typename T> void zip(int = zap([] { })) { }
    }
    template <typename T> void foo(const T &) { }
  }
  void bar() { 
    J::K::zip<long>(); 
    /*Accroding to the above wording,the invoke just like:  
      => J::K::zip<long>(zap([] { })); 
   */
  }

Se o zip não era um modelo, a pesquisa dependente de argumento resolve com êxito a pesquisa de foo em todas as implementações testadas; no entanto, há variação de implementação no tratamento do exemplo, conforme escrito.

Proposta de resolução (setembro de 2013):

Altere 17.8.1 [temp.inst] parágrafo 13 da seguinte maneira:
Se um modelo de função f for chamado de uma maneira que exija a utilização de um argumento padrão, os nomes dependentes serão consultados, as restrições semânticas serão verificadas e a instanciação de qualquer modelo usado no argumento padrão é feito como se o argumento padrão tivesse sido um inicializador usado em uma especialização de modelo de função com o mesmo escopo, os mesmos parâmetros de modelo e o mesmo acesso que o modelo de função f usado nesse ponto, exceto que o escopo no qual um tipo de fechamento é declarado (8.1.5 [expr.prim.lambda]) - e, portanto, seus namespaces associados - permanece como determinado no contexto da definição para o argumento padrão. Essa análise é chamada instanciação de argumento padrão. O argumento padrão instanciado é então usado como o argumento de f.

Observe a parte enfatizada, se eu não me interpretem mal, isso significa que se a peça comentário enfatizou fora, a foonão poderia ser consultados pelo argumento olhar dependente até porque o argumento [] { }que namespace é nem Jnem K, assumir a forma dentro function barcomo J::K::zip<long>(zap([] { }) /*default argument*/);, Então accroding para [ expr.prim.lambda] parágrafo 3 o namespace de [] { }está em fuction bare nesse escopo, não há nenhum foo, portanto a parte enfatizada é para esse caso que considera o namespace de [] { }dentro zapcomo o mesmo que zap, significa que o namespace de [] { }é K, Agora o foopode ser encontrado no espaço para nome paiJpor regras de pesquisa dependentes de argumento, Até agora, se eu interpretar mal essas regras, corrija-me. a outra visão de ponto é que o argumento padrão é avaliado sempre que a função é chamada, mesmo que o padrão seja não dependente . código a seguir:

#include <iostream>
struct A {

};
template<typename T>
int func(T, float) {  //#a
    std::cout << "float" << std::endl;
    return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1

}
template<typename T>
int func(T, int) {  //#b
    std::cout << "int" << std::endl;
    return 0;
}
int main() {
    test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
    std::getchar();
}

Embora o argumento padrão funcnão seja dependente, ele deve ser determinado sempre que a função testfor chamada e eu testo o código em alguns complementadores.
Toda a versão do relatório MSVC "int", relatório gcc "float", relatório clang "float", que diabos? De acordo com o relatório gcc ou clang, parece que isso funcé determinado em #1e MSVC provou que isso funcé determinado em #2. se o MSVC estiver errado, isso significa que o argumento padrão não dependente pode ser determinado no número 1 e não é necessário determinar sempre que a função é chamada, por que a parte enfatizada precisa ser adicionada? (Se eu entendi a parte enfatizada corretamente, o objetivo é manter o namespace do tipo de fechamento dentro do argumento padrão consistente se a expressão lambda estiver no ponto da declaração da função ou no ponto de chamada ). Se eu não entendo essas regras, como interpretá-las corretamente?

ATUALIZAR:

a versão 9.1 ou superior do gcc não pode cumprir o código mencionado no # 1664, ele reportará um erro ( o resultado da conformidade )

Questões:

1.O argumento padrão não dependente do modelo de função ou da função não-modelo precisa ser determinado sempre que a função correspondente é chamada?

2. o que significa "a definição para o argumento padrão"? Esta redação é estritamente? ( Em outras palavras, meu entendimento é: que regras adicionais as regras adicionadas desejam expressar é que o espaço para nome do tipo de fechamento é o de uma declaração de função em que contém um argumento padrão que contém a expressão lambda correspondente, certo? se meu entendimento sobre isso estiver errado, corrija-me )

jack X
fonte
1
"mesmo escopo ... como o do modelo de função fusado" na verdade parece que a especialização hipotética estaria no espaço de nomes J::K, não na definição de bar, mas não vejo como a frase alterada faria alguma diferença.
aschepler 15/03
Entendo tudo isso, mas não acho que tenhamos concluído a mesma coisa com o texto antes da parte alterada / em negrito.
aschepler 15/03
1
"Agora, começo uma recompensa por uma pergunta adicional." Não é assim que o StackOverflow funciona. Se você tiver uma nova pergunta, faça uma nova pergunta. Uma pergunta por pergunta. Revirei sua edição.
Barry
@ Barry Acho que a pergunta atualizada pertence a "Como entender a proposta de resolução de # 1664"
jack X
Desculpe, você precisa postar novas perguntas . O Stack Overflow está aqui para criar um repositório de perguntas úteis e suas respostas para futuros visitantes . Agora que sua primeira pergunta tem uma resposta, vemos esta página como um todo. Por favor, respeite o trabalho que o respondente colocou no fornecimento de uma solução e coloque novas perguntas em novas postagens. Alterar (expandir) o problema aqui invalidaria sua resposta e tornaria sua postagem fora do tópico (fechada como "precisa de mais foco").
Martijn Pieters

Respostas:

3

Os argumentos padrão são avaliados toda vez que são chamados, mas esta é uma propriedade de tempo de execução: as chamadas são contadas não pela linha de origem, mas pelo fluxo de controle real. Separadamente, um argumento padrão para uma função de modelo é considerado uma definição e é instanciado quando necessário, até uma vez por especialização da função (com a condição usual de vários pontos de instanciação que devem concordar). O CWG1664 era uma questão muito restrita, com base em como essa instanciação foi redigida : ao introduzir um modelo de função fictício , deixou em aberto a possibilidade de a declaração do lambda “fisicamente” se mover. A correção realmente afeta apenas ADL.

Seu funcexemplo ilustra as regras usuais de pesquisa de nome nos modelos: não importa quantas vezes e onde test o argumento padrão é instanciado, funcele não é um nome dependente e, portanto, encontra func(T,float)(sempre). É notório que a MSVC nunca implementou essa regra corretamente (porque, para ser justo, sua implementação é anterior à regra e eles só recentemente começaram a reescrita necessária (e quase completa) do suporte ao modelo).

Enquanto isso, o GCC recente está claramente com problemas com o exemplo CWG1664: observe que reclama de footer sido usado, mas não definido, contradizendo a { }mensagem de erro claramente visível e sua mensagem anterior sobre não encontrá-lo.

Davis Herring
fonte
Tento resumir meu entendimento sobre suas respostas. Para o argumento padrão, se eles são nomes não dependentes, os nomes são vinculados conforme escritos nesse ponto (como [dcl.fct.default] / 5 disse), em vez disso, os nomes dependentes são necessários para procurar o nome sempre que a função chamado. 2.Para uma expressão lambda, porque não é um nome, não pode ser vinculado, mesmo que o argumento padrão não seja dependente; portanto, se não houver parte enfatizada na restrição, o tipo de fechamento terá um espaço de nome diferente para o ponto de chamada onde o espaço para nome o cobre. Você concorda?
jack X
Outra pergunta das suas respostas que preciso falar individualmente é: "Especialização de modelo de função fictícia" se refere a "J :: K :: zip <long> ();", em vez da definição de "J :: K :: zip <long>> () "no ponto de instanciação?
jack X
@jackX: É isso e [temp.nondep] / 1 para nomes não dependentes; nomes dependentes são procurados para cada instanciação, independentemente de aparecerem ou não em um argumento padrão. O tipo de fechamento pertenceria à especialização de modelo de função fictícia (que não é uma chamada nem qualquer definição real de zip).
Davis Herring
então, a "especialização do modelo de função fictícia" se refere a algo como "J :: K :: zip <long> ();"? e qual é a eficácia de "exceto que o escopo em que um tipo de fechamento é declarado", acho que a frase a seguiu: "e, portanto, seus namespaces associados - permanecem como determinados no contexto da definição para o argumento padrão" é suficiente para ADL
jack X
@jackX: Acabei de dizer que isso não se refere. O escopo é a regra real: os espaços para nome são um corolário, mesmo se eles carregam toda a importação.
Davis Herring