Jakie jest znaczenie tokena „……”? tj. operator podwójnego wielokropka w pakiecie parametrów

110

Przeglądając aktualną implementację nowych nagłówków C ++ 11 w gcc, natknąłem się na token „......”. Możesz sprawdzić, czy poniższy kod kompiluje się dobrze [przez ideone.com].

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

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

Więc jakie jest znaczenie tego tokena?

edycja: Wygląda na tak przycięty „......” w tytule pytania do „…”, naprawdę miałem na myśli „......”. :)

Vitus
źródło
wskazówka: ...następuje po nim ....
Alexandre C.
5
Czy nie jest to bardziej jak U...następuje .... Niemniej jednak bardzo dziwne.
edA-qa mort-ora-y
1
Uwaga: można to znaleźć w programie <functional>i <type_traits>, zawsze w kontekście listy argumentów funkcji wewnątrz parametru szablonu.
Potatoswatter
Jedynym sposobem, w jaki utknąłem w tytule, było wstawienie spacji między ... mam nadzieję, że uczyni to bardziej przejrzystym dla czytelników.
Matthieu M.
@Matthieu M .: Dzięki, dużo lepiej!
Vitus

Odpowiedzi:

79

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 );
}
Potatoswatter
źródło
Tak to prawda. T (U ..., ...) jednak również kompiluje się dobrze; być może chcieli zaoszczędzić trochę miejsca. :)
Vitus
1
Ale co by to oznaczało? W jaki sposób kompilator może stwierdzić, gdzie kończą się _ArgTypes, a zaczynają się niektóre „dodatkowe” parametry?
Bo Persson
12
@Bo Persson: std::is_functions valuemusi być prawdziwe, nawet jeśli funkcja jest jedną z C varargs i ponieważ T (U ...) nie pasuje do takiej funkcji, potrzebujesz tego szaleństwa. Np. Int f (int, char, ...) dopasowuje T (U ......) dokładnie z T = int, U = {int, char} i tokenem varargs „...”.
Vitus
4
„To jest wewnątrz abstrakcyjnego-deklaratora” -> nie są częścią abstrakcyjnego deklaratora ostatniego parametru tej samej listy typów parametrów. Np. void (int...)Tutaj, ...nie jest częścią abstrakcyjnego-deklaratora int, stąd jest synonimem void(int, ...). Jeśli napisałbyś void(T...)i Tjest pakietem parametrów szablonu, ...byłby częścią abstrakcyjnego deklaratora, a zatem nie byłby równoważny z void(T, ...).
Johannes Schaub - litb
2
„Co więcej, va_begin () w <cstdarg> wymaga parametru przed listą varargs, więc prototyp f (...) specjalnie dozwolony przez C ++ jest bezużyteczny.” - Nie ma sensu tylko wtedy, gdy chcesz wiedzieć, jakie argumenty zostały przekazane. f(...)jest często używany jako przeciążenie funkcji rezerwowej w metaprogramowaniu szablonów, gdzie ta informacja nie jest konieczna (a funkcja nawet nie jest wywoływana).
4

w vs2015 przecinek oddzielający jest niezbędny w wersji szablonu:

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

przykładowa instancja to:

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

pozdrawiam, FM.

Red.Wave
źródło
Właśnie to zauważyłem, nadal to się dzieje. Raport o błędzie na developercommunity.visualstudio.com/content/problem/437260/… .
egyik
Dobrze wiedzieć. Jakieś odniesienia lub cytaty ze stanowisk na ten temat?
Red.Wave
.سلام ببخشید نمیدانم
egyik
To jest forum publiczne. Pozwól ludziom przeczytać, co myślisz. PLZ zachowuje język ojczysty dla prywatnych wiadomości. سپاس.
Red.Wave
OK. Nie jestem ekspertem od standardu - myślę, że inni omówili to trochę powyżej. Jeśli komuś zależy na skomentowaniu raportu o problemie firmy Microsoft, może to podnieść jego priorytet. Raport pokazuje, że clang i gcc pozwalają na to, czego VC ++ nie robi, więc myślę, że prawdopodobnie jesteśmy na dość mocnym gruncie.
egyik