Templat `apply` dikompilasi di g++ tetapi tidak di clang++ dan vc++

Kode berikut berhasil dikompilasi di g++ 7.2.0 (tanda kompilasi adalah -std=c++14 -Wall -Wextra -Werror -pedantic-errors ), namun gagal dikompilasi dalam clang++ 5.0.0 (dengan flag yang sama, -std=c++14 -Wall -Wextra -Werror -pedantic-errors) dan vc++ 15.4 (flag kompilasi adalah /EHsc /Za /std:c++14 /permissive-):

template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
    template <typename... FreeArguments>
    using type = Functor<FixedArguments..., FreeArguments...>;
};

template <typename, typename>
struct Bar{};

template <template <typename...> class>
struct Foo{};

int main()
{
    (void)Foo<apply<Bar, int, char>::type>{};
}

Perilaku kompiler manakah yang memenuhi standar? Bagaimana template apply tersebut dapat diubah agar dapat dikompilasi di clang++ juga?

Pesan kesalahan dentang++:

5 : <source>:5:15: error: too many template arguments for class template 'Bar'
        using type = Functor<FixedArguments..., FreeArguments...>;
                     ^                          ~~~~~~~~~~~~~~~~~
16 : <source>:16:15: note: in instantiation of template class 'apply<Bar, int, char>' requested here
    (void)Foo<apply<Bar, int, char>::type>{};
              ^
9 : <source>:9:8: note: template is declared here
struct Bar{};

pesan kesalahan vc++:

5 : <source>(5): error C2977: 'Bar': too many template arguments
9 : <source>(9): note: see declaration of 'Bar'
16 : <source>(16): note: see reference to class template instantiation 'apply<Bar,int,char>' being compiled

person Constructor    schedule 22.11.2017    source sumber
comment
Kesalahan apa yang diberikan Dentang?   -  person piwi    schedule 22.11.2017
comment
Pesan kesalahan @piwi clang++ dan vc++ tersedia di tautan ke godbolt.org dalam pertanyaan. Saya telah menambahkannya ke pertanyaan juga.   -  person Constructor    schedule 22.11.2017
comment
Saya buruk tidak melihatnya sebelum memposting komentar.   -  person piwi    schedule 22.11.2017
comment
Tambahkan parameter templat ke-3 typename... ke Bar?   -  person songyuanyao    schedule 22.11.2017
comment
@songyuanyao Bar dan Foo adalah contoh kelas di sini untuk mendemonstrasikan masalahnya. Ciri-cirinya diberikan dari luar. Faktanya, ini adalah MWE terkecil yang mendemonstrasikan masalah dalam beberapa kode template yang kompleks.   -  person Constructor    schedule 22.11.2017
comment
Saya tidak tahu mana yang sesuai tetapi bagi saya kesalahan tersebut tampaknya relevan; ketika menyimpulkan alias templat apply<Bar, int, char>::type, Bar<int, char> sepenuhnya terspesialisasi, oleh karena itu tidak ada cara tipe tambahan dapat dimasukkan ke Functor nanti.   -  person piwi    schedule 22.11.2017
comment
[temp.res]/8.3. Ini adalah NDR yang buruk. Namun lihat masalah inti 2067.   -  person T.C.    schedule 22.11.2017
comment
@T.C. Terima kasih atas referensi Anda! Tetapi bagaimana kode tersebut dapat diperbaiki untuk dikompilasi?   -  person Constructor    schedule 22.11.2017


Jawaban (2)


catatan: setelah melihat ini, jawaban ini akan benar jika Bar adalah templat alias dan bukan templat kelas. Solusinya berhasil tetapi karena alasan lain. Lihat jawaban Konstruktor untuk jawaban OP yang benar.

Masalah ini dikenal sebagai 'cacat alias' dan kami menghadapi banyak tantangan dalam penerapan kvasir::mpl. Masalahnya adalah Bar mengambil tepat dua parameter tetapi sizeof...(FixedArguments)+sizeof...(FreeArguments) dapat menambahkan hingga 2.

Kompiler dapat mencoba dan melacak potensi arity melalui semua panggilan alias dan hanya mengeluarkan kesalahan ketika pengguna benar-benar meneruskan sesuatu selain 2 atau dapat "dengan penuh semangat" memberikan kesalahan hanya dengan membuktikan bahwa kesalahan dapat terjadi.

Solusi yang menurut saya efektif dalam mengatasi hal ini adalah membuat panggilan alias bergantung pada ukuran input https://godbolt.org/g/PT4uaE

template<bool>
struct depends{
    template<template<typename...> class F, typename...Ts>
    using f = F<Ts...>;
};

template<>
struct depends<false>{
    template<template<typename...> class F, typename...Ts>
    using f = void;
};

template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
    template <typename... FreeArguments>
    using type = typename depends<(sizeof...(FixedArguments)+sizeof...(FreeArguments) == 2)>::template f<Functor, FixedArguments..., FreeArguments...>;
};

template <typename, typename>
struct Bar{};

template <template <typename...> class>
struct Foo{};

int main()
{
    (void)Foo<apply<Bar, int, char>::type>{};
}

Perlu dicatat bahwa membatasi tepat dua tidak diperlukan pada semua kompiler yang telah saya uji, seseorang dapat dengan mudah membatasi menjadi sizeof...(FixedArguments)+sizeof...(FreeArguments) != 100000 dan kompiler akan tetap menganggapnya hanya mengeluarkan kesalahan jika segala sesuatunya benar-benar tidak berjalan dengan baik secara konkret panggilan.

Saya sebenarnya ingin meningkatkan model mental saya tentang cara kerjanya secara internal untuk menghasilkan solusi yang lebih cepat, di kvasir::mpl kami saat ini bereksperimen dengan melacak arity secara manual di belakang layar untuk menghilangkan panggilan dependen yang melakukan hal tersebut. memperlambat segalanya sedikit.

person odinthenerd    schedule 22.11.2017
comment
Terima kasih. Bisakah Anda menyalin kode solusi ke jawaban Anda? - person Constructor; 22.11.2017

Sebagai @T.C. dicatat di komentar pada pertanyaan kode tersebut salah bentuk (tidak diperlukan diagnostik).

Standar C++14, bagian "Resolusi nama" [temp.res], paragraf 8:

Jika setiap spesialisasi yang valid dari templat variadik memerlukan paket parameter templat yang kosong, maka templat tersebut tidak berbentuk, dan tidak diperlukan diagnostik.

Draf terbaru standar C++, bagian "Resolusi nama" [temp.res], paragraf 8.3:

...Program ini salah bentuk, tidak diperlukan diagnostik, jika:

  • ...
  • setiap spesialisasi yang valid dari templat variadik memerlukan paket parameter templat kosong...

Informasi tambahan: Masalah Inti 2067.

Sesuai dengan persyaratan standar solusi sederhana tersebut dapat ditulis:

template <template <typename...> class Functor, typename... Arguments>
struct invoke
{
    using type = Functor<Arguments...>;
};

template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
    template <typename... FreeArguments>
    using type = typename invoke<Functor, FixedArguments..., FreeArguments...>::type;
};

Demo langsung

Pembaruan: Sebagai @odinthenerd dicatat di komentar solusi ini menggunakan tipe tambahan yang menyebabkan kompilasi program menjadi lebih lambat.

person Constructor    schedule 22.11.2017
comment
Saya pikir ini sebenarnya jawaban yang benar dan solusi saya berhasil. Namun perlu dicatat bahwa solusi tersebut menciptakan suatu tipe dan oleh karena itu, akan lebih lambat. - person odinthenerd; 27.11.2017
comment
@odinthenerd Saya telah menambahkan catatan yang diperlukan. - person Constructor; 29.11.2017
comment
@odinthenerd Apakah Anda tidak keberatan saya menerima jawaban saya, bukan jawaban Anda? - person Constructor; 29.11.2017