Pacote de parâmetros do modelo de function não no final da lista de parâmetros

O código a seguir compila e executa ok.

void foo() { } template  void foo(T x, Args... args) { cout << x << endl; foo(args...); } // inside main() foo(1,1,1); 

Este outro código não compila:

 void foo() { } template  void foo(Args... args, T x) { foo(args...); cout << x << endl; } // inside main() foo(1,1,1); 

O compilador diz que não há function correspondente para chamar a foo(1,1,1) e diz que foo(Args... args, T x) é um candidato, mas a dedução / substituição de argumento de modelo falhou, porque o candidato espera 1 argumento, mas 3 foram fornecidos.

Existe alguma ambiguidade com esta situação que nenhum compilador pode manipular? Este erro de compilation parece apenas ilógico para mim. Talvez isso não esteja de acordo, de propósito, com o padrão C ++?

A parte interessante da mensagem de erro da Clang é:

 main.cpp:11:6: note: candidate template ignored: couldn't infer template argument 'T' void foo(Args... args, T x) { ^ 

O problema é que o pacote de parâmetro Args... ocorre antes de T

Args... é “ganancioso” e, portanto, nenhum parâmetro é deixado para o compilador deduzir T , portanto ele falha.

Citando o padrão (ênfase minha):

[temp.param] / 11

Um pacote de parâmetros de modelo de um modelo de function não deve ser seguido por outro parâmetro de modelo, a menos que esse parâmetro de modelo possa ser deduzido da lista de tipos de parâmetros do modelo de function ou tenha um argumento padrão. [Exemplo:

 ... // U can be neither deduced from the parameter-type-list nor specified template void f() { } // error template void g() { } // error 

– exemplo final

(Esta resposta é baseada nos comentários de @ JohannesSchaub-litb )

De acordo com o padrão, o pacote de parâmetros de modelo não é dedutível se for usado em um pacote de parâmetros de function que não esteja no final da lista de parâmetros.

§14.8.2.1 / 1 Deduzindo argumentos de modelo de uma chamada de function [temp.deduct.call] :

Quando um pacote de parâmetros de function aparece em um contexto não deduzido ([temp.deduct.type]), o tipo desse pacote de parâmetros nunca é deduzido. [Exemplo:

 template void g1(Types ..., T1); void h(int x, float& y) { const int z = x; g1(x, y, z); // error: Types is not deduced g1(x, y, z); // OK, no deduction occurs } 

– exemplo final

E sobre contexto não deduzido, §14.8.2.5 / 5 Deduzindo argumentos de modelo de um tipo [temp.deduct.type] :

Um pacote de parâmetros de function que não ocorre no final da lista de declaração de parâmetros.

Então a razão direta de foo(1,1,1); falha é que o parâmetro de modelo Args não é deduzido, o que é necessário para tornar a invocação de function válida.

Para explicar a mensagem de erro, um pacote de parâmetro de modelo não deduzido será deduzido a uma seqüência vazia de argumentos de modelo [1] , isso significa que ele será omitido. Então foo(1,1,1); falhou porque o número de argumentos não corresponde, foi o que o compilador reclamou.

Assim como o exemplo do padrão mostrado, você poderia especificar o argumento do modelo explicitamente para evitar a dedução do tipo, mesmo que ele não atenda à intenção original do seu código. Tal como:

 template  void foo(Args... args, T x) { } int main() { // inside main() foo(1, 1, 1); } 

Veja algumas informações adicionais.


[1] Não consigo encontrar expressão direta sobre isso no padrão. O mais próximo deles é o seguinte : “Um pacote de parâmetros do modelo final ([temp.variadic]) não deduzido de outra maneira será deduzido a uma sequência vazia de argumentos de modelo.”