¿Cuál es el significado de la ficha "... ..."? es decir, operador de elipsis doble en el paquete de parámetros

110

Mientras navegaba por la implementación actual de gcc de los nuevos encabezados de C ++ 11, me topé con el token "......". Puede comprobar que el siguiente código se compila bien [a través de ideone.com].

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

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

Entonces, ¿cuál es el significado de esta ficha?

editar: Parece TAN recortado "......" en el título de la pregunta a "...", realmente quise decir "......". :)

Vitus
fuente
pista: le ...sigue ....
Alexandre C.
5
¿No es más como U...seguido de .... No obstante, muy extraño.
edA-qa mort-ora-y
1
Nota: Esto se puede encontrar en <functional>y <type_traits>, siempre en el contexto de una lista de argumentos de función dentro de un parámetro de plantilla.
Potatoswatter
La única forma que encontré para hacerlo pegado en el título fue poner un espacio en el medio ... espero que lo deje más claro para los lectores.
Matthieu M.
@Matthieu M .: ¡Gracias, mucho mejor!
Vitus

Respuestas:

79

Cada instancia de esa rareza se empareja con un caso de una sola elipsis 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; };

Supongo que la elipsis doble tiene un significado similar a _ArgTypes..., ..., es decir, una expansión de plantilla variada seguida de una lista de varargs de estilo C.

Aquí hay una prueba que respalda esa teoría ... Creo que tenemos un nuevo ganador para el peor pseudo operador de la historia.

Editar: Esto parece ser conforme. §8.3.5 / 3 describe una forma de formar la lista de parámetros como

lista de declaraciones de parámetros opt ... opt

Entonces, la elipsis doble está formada por una lista de declaración de parámetros que termina con un paquete de parámetros, seguida de otra elipsis.

La coma es puramente opcional; §8.3.5 / 4 dice

Donde sintácticamente correcto y donde "..." no es parte de un declarador abstracto, ", ..." es sinónimo de "...".

Esto está dentro de un declarador abstracto, [editar] pero Johannes hace un buen punto de que se refieren a un declarador abstracto dentro de una declaración de parámetro. Me pregunto por qué no dijeron "parte de una declaración de parámetro" y por qué esa oración no es solo una nota informativa ...

Además, va_begin()in <cstdarg>requiere un parámetro antes de la lista de varargs, por lo que el prototipo f(...)específicamente permitido por C ++ es inútil. Hacer referencias cruzadas con C99, es ilegal en simple C. Entonces, esto es de lo más extraño.

Nota de uso

A pedido, aquí hay una demostración de la doble elipsis:

#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
fuente
Sí, eso es correcto. T (U ..., ...) sin embargo también compila bien; tal vez querían ahorrar algo de espacio. :)
Vitus
1
Pero, ¿qué significa eso? ¿Y cómo puede el compilador saber dónde terminan _ArgTypes y dónde comienzan algunos parámetros "extra"?
Bo Persson
12
@Bo Persson: std::is_function's valuedebe ser verdadero incluso si la función es C varargs one y porque T (U ...) no coincide con dicha función, necesitas esta locura. Por ejemplo, int f (int, char, ...) coincide con T (U ......) exactamente con T = int, U = {int, char} y el token varargs "...".
Vitus
4
"Esto está dentro de un declarador abstracto" -> significan que no es parte del declarador abstracto del último parámetro de esa misma lista de tipos de parámetros. Por ejemplo void (int...), aquí ...no es parte del declarador abstracto int, por lo que es sinónimo de void(int, ...). Si escribiera void(T...)y Tes un paquete de parámetros de plantilla, ...sería parte del declarador abstracto y, por lo tanto, no sería equivalente a void(T, ...).
Johannes Schaub - litb
2
"Además, va_begin () en <cstdarg> requiere un parámetro antes de la lista de varargs, por lo que el prototipo f (...) específicamente permitido por C ++ es inútil". - Solo es inútil si quieres saber qué argumentos se pasaron. f(...)se usa mucho como una sobrecarga de funciones de respaldo en la metaprogramación de plantillas, donde esta información no es necesaria (y donde la función ni siquiera se llama).
4

en vs2015 separar la coma es esencial en la versión de plantilla:

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

una instanciación de ejemplo es:

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

saludos, FM.

Red.Wave
fuente
También me di cuenta de esto, todavía sucede. Informe de error en developercommunity.visualstudio.com/content/problem/437260/… .
egyik
Bueno saber. ¿Alguna referencia o cita a estandartes sobre esto?
Red.Wave
.سلام ببخشید نمیدانم
egyik
Esto es un foro público. Deja que la gente lea lo que piensas. PLZ mantiene el idioma nativo para los mensajes privados. سپاس.
Red.Wave
OK entonces. No soy un experto en el estándar; creo que otros lo han cubierto con cierto detalle anteriormente. Si alguien quiere comentar sobre el informe de problemas de Microsoft, entonces podría aumentar su prioridad. El informe muestra clang y gcc permitiendo lo que VC ++ no permite, así que creo que probablemente estemos en un terreno bastante sólido.
egyik