Apa arti token “……”? yaitu operator elipsis ganda pada paket parameter

110

Saat menelusuri implementasi gcc saat ini untuk header C ++ 11 yang baru, saya menemukan token "......". Anda dapat memeriksa, bahwa kode berikut dapat dikompilasi dengan baik [via ideone.com].

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

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

Lantas, apa artinya token ini?

edit: Sepertinya SO dipangkas "......" pada judul pertanyaan menjadi "...", maksud saya benar-benar "......". :)

Vitus
sumber
petunjuk: itu ...diikuti oleh ....
Alexandre C.
5
Bukankah lebih seperti U...diikuti oleh .... Sangat aneh.
edA-qa mort-ora-y
1
Catatan: Ini bisa ditemukan di <functional>dan <type_traits>, selalu dalam konteks daftar argumen fungsi di dalam parameter templat.
Potatoswatter
satu-satunya cara yang saya temukan untuk membuatnya melekat di judul adalah dengan memberi spasi di antara ... harap ini membuatnya lebih jelas bagi pembaca.
Matthieu M.
@ Matthieu M .: Terima kasih, jauh lebih baik!
Vitus

Jawaban:

79

Setiap contoh dari keanehan itu dipasangkan dengan kasus elipsis tunggal biasa.

  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; };

Dugaan saya adalah bahwa elipsis ganda memiliki arti yang sama _ArgTypes..., ..., yaitu perluasan template variadic diikuti oleh daftar varargs gaya C.

Ini adalah tes yang mendukung teori itu… Saya pikir kita memiliki pemenang baru untuk operator semu terburuk yang pernah ada.

Sunting: Ini tampaknya konforman. §8.3.5 / 3 menjelaskan satu cara untuk membentuk daftar parameter sebagai

parameter-deklarasi-daftar opt ... opt

Jadi elipsis ganda dibentuk oleh daftar-deklarasi parameter yang diakhiri dengan paket parameter, diikuti oleh elipsis lain.

Koma murni opsional; §8.3.5 / 4 memang mengatakan

Di mana secara sintaksis benar dan di mana "..." bukan bagian dari deklarator-abstrak, ", ..." adalah sinonim dengan "...".

Ini ada di dalam deklarator-abstrak, [edit] tetapi Johannes membuat poin yang bagus bahwa mereka merujuk ke deklarator-abstrak dalam deklarasi parameter. Saya bertanya-tanya mengapa mereka tidak mengatakan "bagian dari deklarasi parameter", dan mengapa kalimat itu bukan hanya catatan informatif…

Selain itu, va_begin()in <cstdarg>membutuhkan parameter sebelum daftar varargs, sehingga prototipe yang f(...)secara khusus diizinkan oleh C ++ tidak berguna. Referensi silang dengan C99, ini ilegal di dataran C. Jadi, ini paling aneh.

Catatan penggunaan

Berdasarkan permintaan, berikut demonstrasi elipsis ganda:

#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
sumber
Ya itu betul. T (U ..., ...) bagaimanapun mengkompilasi dengan baik, juga; mungkin mereka ingin menghemat ruang. :)
Vitus
1
Tapi apa artinya itu? Dan bagaimana kompilator dapat mengetahui di mana _ArgTypes berakhir dan beberapa parameter "ekstra" dimulai?
Bo Persson
12
@Bo Persson: std::is_function's valueharus benar bahkan jika fungsi ini C varargs satu dan karena T (U ...) yang tidak cocok untuk fungsi tersebut, Anda perlu kegilaan ini. Misalnya int f (int, char, ...) cocok dengan T (U ......) persis dengan T = int, U = {int, char} dan token varargs "...".
Vitus
4
"Ini ada di dalam deklarator-abstrak" -> artinya bukan bagian dari deklarator abstrak dari parameter terakhir dari daftar jenis parameter yang sama. Misalnya di void (int...)sini, the ...bukan bagian dari deklarator-abstrak int, oleh karena itu ia identik dengan void(int, ...). Jika Anda akan menulis void(T...)dan Tmerupakan paket parameter template, ...akan menjadi bagian dari deklarator-abstrak, dan karenanya tidak akan setara dengan void(T, ...).
Johannes Schaub - litb
2
"Lebih lanjut, va_begin () di <cstdarg> memerlukan parameter sebelum daftar varargs, jadi prototipe f (...) yang secara khusus diizinkan oleh C ++ tidak berguna." - Tidak ada gunanya jika Anda ingin tahu argumen apa yang disampaikan. f(...)banyak digunakan sebagai kelebihan beban fungsi fallback dalam metaprogramming template, di mana informasi ini tidak diperlukan (dan di mana fungsinya bahkan tidak benar-benar dipanggil).
4

pada vs2015, memisahkan koma sangat penting dalam versi template:

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

contoh contoh adalah:

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

salam, FM.

Gelombang Merah
sumber
Saya hanya memperhatikan ini juga, itu masih terjadi. Laporan bug di developercommunity.visualstudio.com/content/problem/437260/… .
egyik
Senang mendengarnya. Adakah referensi atau kutipan untuk standads tentang ini?
Red. Gelombang
.سلام ببخشید نمیدانم
egyik
Ini adalah forum publik. Biarkan orang membaca apa yang Anda pikirkan. PLZ tetap menggunakan lang asli untuk pesan pribadi. سپاس.
Red. Gelombang
Oke. Saya bukan ahli tentang standar ini - saya pikir orang lain telah membahasnya secara mendetail di atas. Jika ada yang peduli untuk mengomentari laporan masalah Microsoft maka itu mungkin menaikkan prioritasnya. Laporan tersebut menunjukkan clang dan gcc mengizinkan apa yang tidak VC ++ jadi saya pikir kami mungkin berada di tempat yang cukup kuat.
egyik