Qual é o significado de token “……”? ou seja, operador de elipse dupla no pacote de parâmetros

110

Ao navegar pela implementação atual do gcc de novos cabeçalhos C ++ 11, me deparei com o token "......". Você pode verificar se o código a seguir é compilado corretamente [via ideone.com].

template <typename T>
struct X
{ /* ... */ };

template <typename T, typename ... U>
struct X<T(U......)> // this line is the important one
{ /* ... */ };

Então, qual é o significado deste token?

editar: Parece que TÃO aparado "......" no título da pergunta para "...", eu realmente quis dizer "......". :)

Vitus
fonte
dica: é ...seguido por ....
Alexandre C.
5
Não é mais como U...seguido por .... Mesmo assim, muito estranho.
edA-qa mort-ora-y
1
Nota: Isso pode ser encontrado em <functional>e <type_traits>, sempre no contexto de uma lista de argumentos de função dentro de um parâmetro de modelo.
Potatoswatter
a única maneira que encontrei de prendê-lo no título foi colocar um espaço no meio ... espero que fique mais claro para os leitores.
Matthieu M.
@Matthieu M .: Obrigado, muito melhor!
Vitus

Respostas:

79

Cada instância dessa estranheza é emparelhada com um caso de uma única elipse regular.

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...) const>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......) const>
    { typedef _Res result_type; };

Meu palpite é que a elipse dupla é semelhante em significado a _ArgTypes..., ..., ou seja, uma expansão de modelo variadic seguida por uma lista de varargs de estilo C.

Aqui está um teste que apóia essa teoria ... Acho que temos um novo vencedor para o pior pseudo-operador de todos os tempos.

Edit: Isso parece estar em conformidade. §8.3.5 / 3 descreve uma maneira de formar a lista de parâmetros como

parâmetro-declaração-lista opt ... opt

Assim, a elipse dupla é formada por uma lista de declaração de parâmetros terminando com um pacote de parâmetros, seguido por outras reticências.

A vírgula é puramente opcional; §8.3.5 / 4 diz

Onde sintaticamente correto e onde “...” não faz parte de um declarador abstrato, “, ...” é sinônimo de “...”.

Isso está dentro de um declarador abstrato, [editar] mas Johannes afirma que eles estão se referindo a um declarador abstrato dentro de uma declaração de parâmetro. Eu me pergunto por que eles não disseram "parte de uma declaração de parâmetro" e por que essa frase não é apenas uma nota informativa ...

Além disso, va_begin()in <cstdarg>requer um parâmetro antes da lista de varargs, de modo que o protótipo f(...)especificamente permitido por C ++ é inútil. A referência cruzada com C99 é ilegal na planície C. Então, isso é muito bizarro.

Nota de uso

A pedido, aqui está uma demonstração da elipse dupla:

#include <cstdio>
#include <string>

template< typename T >
T const &printf_helper( T const &x )
    { return x; }

char const *printf_helper( std::string const &x )
    { return x.c_str(); }

template< typename ... Req, typename ... Given >
int wrap_printf( int (*fn)( Req... ... ), Given ... args ) {
    return fn( printf_helper( args ) ... );
}

int main() {
    wrap_printf( &std::printf, "Hello %s\n", std::string( "world!" ) );
    wrap_printf( &std::fprintf, stderr, std::string( "Error %d" ), 5 );
}
Potatoswatter
fonte
Sim está certo. T (U ..., ...) porém compila bem, também; talvez eles quisessem economizar algum espaço. :)
Vitus
1
Mas o que isso significa? E como o compilador pode dizer onde _ArgTypes termina e alguns parâmetros "extras" começam?
Bo Persson,
12
@Bo Persson: std::is_function's valuedeve ser verdadeiro mesmo que a função seja C varargs um e porque T (U ...) não é compatível com tal função, você precisa dessa loucura. Por exemplo, int f (int, char, ...) corresponde a T (U ......) exatamente com T = int, U = {int, char} e o token varargs "...".
Vitus
4
"Isto está dentro de um declarador abstrato" -> eles significam que não fazem parte do declarador abstrato do último parâmetro da mesma lista de tipo de parâmetro. Por exemplo void (int...), aqui, o ...não faz parte do declarador abstrato int, portanto, é sinônimo de void(int, ...). Se você escrever void(T...)e Tfor um pacote de parâmetros de modelo, ...será parte do declarador abstrato e, portanto, não seria equivalente a void(T, ...).
Johannes Schaub - litb
2
"Além disso, va_begin () em <cstdarg> requer um parâmetro antes da lista de varargs, então o protótipo f (...) especificamente permitido por C ++ é inútil." - Só é inútil se você quiser saber quais argumentos foram passados. f(...)é usado fortemente como uma sobrecarga de função de fallback na metaprogramação de template, onde esta informação não é necessária (e onde a função nem mesmo é chamada).
4

no vs2015, separar vírgulas é essencial na versão do modelo:

    template <typename T, typename ... U>
    struct X<T(U...,...)> {};// this line is the important one

um exemplo de instanciação é:

    X<int(int...)> my_va_func;

Atenciosamente, FM.

Red.Wave
fonte
Acabei de perceber isso também, ainda acontece. Relatório de bug em developersercommunity.visualstudio.com/content/problem/437260/… .
egyik
Bom saber. Quaisquer referências ou citações de padrões sobre isso?
Red.Wave
.سلام ببخشید نمیدانم
egyik
Este é um fórum público. Deixe as pessoas lerem o que você pensa. PLZ mantém a linguagem nativa para mensagens privadas. سپاس.
Red.Wave
OK então. Não sou especialista no padrão - acho que outros o abordaram com alguns detalhes acima. Se alguém quiser comentar sobre o relatório de problemas da Microsoft, isso pode aumentar sua prioridade. O relatório mostra o clang e o gcc permitindo o que o VC ++ não permite, então acho que provavelmente estamos em terreno bastante sólido.
egyik