Każde wystąpienie tej osobliwości jest powiązane z przypadkiem zwykłej pojedynczej wielokropka.
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; };
Domyślam się, że podwójny wielokropek ma podobne znaczenie _ArgTypes..., ...
, np. Wariadyczne rozwinięcie szablonu, po którym następuje lista varargs w stylu C.
Oto test potwierdzający tę teorię… Myślę, że mamy nowego zwycięzcę dla najgorszego pseudo-operatora wszechczasów.
Edycja: wydaje się, że jest to zgodne. W §8.3.5 / 3 opisano jeden ze sposobów tworzenia listy parametrów jako
lista-deklaracji parametrów opt ... opt
Tak więc podwójny wielokropek jest tworzony przez listę deklaracji parametrów kończącą się pakietem parametrów, po której następuje kolejny wielokropek.
Przecinek jest całkowicie opcjonalny; §8.3.5 / 4 mówi
Tam, gdzie składniowo poprawne i gdzie „…” nie jest częścią deklaratora abstrakcji, „,…” jest synonimem „…”.
Dzieje się to w ramach abstrakcyjnego-deklaratora, [edytuj], ale Johannes dobrze zauważa, że odnoszą się one do abstrakcyjnego-deklaratora w deklaracji parametru. Zastanawiam się, dlaczego nie powiedzieli „część deklaracji parametru” i dlaczego to zdanie nie jest tylko informacją…
Ponadto va_begin()
in <cstdarg>
wymaga parametru przed listą varargs, więc prototyp f(...)
wyraźnie dozwolony przez C ++ jest bezużyteczny. Powiązanie z C99 jest nielegalne w zwykłym C. To jest najbardziej dziwne.
Uwaga dotycząca użytkowania
Na życzenie przedstawiam podwójną wielokropek:
#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 );
}
...
następuje po nim...
.