คอมไพล์เทมเพลต `apply` ใน g++ แต่ไม่ใช่ใน clang++ และ vc++

รหัสต่อไปนี้ คอมไพล์สำเร็จใน g++ 7.2.0 (แฟล็กการคอมไพล์คือ -std=c++14 -Wall -Wextra -Werror -pedantic-errors ) แต่ไม่สามารถคอมไพล์ใน clang++ 5.0.0 (ด้วยแฟล็กเดียวกัน -std=c++14 -Wall -Wextra -Werror -pedantic-errors) และ vc++ 15.4 (แฟล็กการคอมไพล์คือ /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>{};
}

พฤติกรรมของคอมไพเลอร์ใดที่เป็นไปตามมาตรฐาน เทมเพลต apply ดังกล่าวสามารถเปลี่ยนให้คอมไพล์บน clang++ ได้อย่างไร

ข้อความแสดงข้อผิดพลาด เสียงดังกราว++:

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

ข้อความแสดงข้อผิดพลาด 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

comment
เสียงดังกราวให้ข้อผิดพลาดอะไร?   -  person piwi    schedule 22.11.2017
comment
@piwi clang++ และ vc++ มีข้อความแสดงข้อผิดพลาดอยู่ที่ ลิงก์ ไปที่ godbolt.org ในคำถาม ฉันได้เพิ่มพวกเขาเข้าไปในคำถามด้วย   -  person Constructor    schedule 22.11.2017
comment
แย่ของฉันไม่เห็นมันก่อนที่จะโพสต์ความคิดเห็น   -  person piwi    schedule 22.11.2017
comment
เพิ่มพารามิเตอร์เทมเพลตที่ 3 typename... ถึง Bar?   -  person songyuanyao    schedule 22.11.2017
comment
@songyuanyao Bar และ Foo เป็นคลาสตัวอย่างที่นี่เพื่อแสดงปัญหา ลักษณะของพวกเขาได้รับจากภายนอก ในความเป็นจริง MWE ที่เล็กที่สุดแสดงให้เห็นถึงปัญหาในโค้ดเทมเพลตที่ซับซ้อนบางส่วน   -  person Constructor    schedule 22.11.2017
comment
ฉันไม่สามารถบอกได้ว่าสิ่งใดเป็นไปตามข้อกำหนด แต่สำหรับฉันแล้วข้อผิดพลาดนั้นมีความเกี่ยวข้อง เมื่ออนุมานนามแฝงเทมเพลต apply<Bar, int, char>::type นั้น Bar<int, char> จะมีความเชี่ยวชาญเฉพาะทางอย่างเต็มที่ ดังนั้นจึงไม่มีทางที่จะสามารถป้อนประเภทเพิ่มเติมไปยัง Functor ในภายหลังได้   -  person piwi    schedule 22.11.2017
comment
[temp.res]/8.3 นี่คือ NDR ที่มีรูปแบบไม่ถูกต้อง แต่โปรดดูปัญหาหลัก 2067   -  person T.C.    schedule 22.11.2017
comment
@ที.ซี. ขอบคุณสำหรับการอ้างอิงของคุณ! แต่จะแก้ไขโค้ดดังกล่าวให้คอมไพล์ได้อย่างไร?   -  person Constructor    schedule 22.11.2017


คำตอบ (2)


หมายเหตุ: หลังจากดูสิ่งนี้แล้ว คำตอบนี้จะถูกต้องหาก Bar เป็นเทมเพลตนามแฝงแทนที่จะเป็นเทมเพลตคลาส วิธีแก้ปัญหาใช้งานได้ แต่ด้วยเหตุผลอื่น ดูคำตอบของตัวสร้างสำหรับคำตอบที่ถูกต้องของ OP

ปัญหานี้เรียกว่า 'ข้อบกพร่องของนามแฝง' และเรามีความท้าทายมากมายในการใช้งาน kvasir::mpl ปัญหาคือ Bar รับพารามิเตอร์สองตัวพอดี แต่ sizeof...(FixedArguments)+sizeof...(FreeArguments) สามารถรวมกันเป็นอย่างอื่นนอกเหนือจาก 2 ได้

คอมไพเลอร์สามารถลองและติดตามความสมบูรณ์ที่อาจเกิดขึ้นผ่านการเรียกนามแฝงทั้งหมด และปัญหาเฉพาะข้อผิดพลาดเมื่อผู้ใช้ส่งผ่านบางสิ่งที่นอกเหนือจาก 2 จริง ๆ หรือสามารถ "กระตือรือร้น" ให้ข้อผิดพลาดได้ง่ายๆ โดยการพิสูจน์ว่ามีข้อผิดพลาดเกิดขึ้น

วิธีแก้ไขที่ฉันพบว่ามีประสิทธิภาพในการจัดการกับสิ่งนี้คือการทำให้การเรียกนามแฝงขึ้นอยู่กับขนาดของอินพุต 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>{};
}

ควรสังเกตว่าการจำกัดสองอย่างนั้นไม่จำเป็นสำหรับคอมไพเลอร์ทั้งหมดที่ฉันได้ทดสอบ สิ่งหนึ่งที่สามารถจำกัดได้ง่าย ๆ ให้เป็น sizeof...(FixedArguments)+sizeof...(FreeArguments) != 100000 และคอมไพเลอร์จะยังคงดำเนินการเฉพาะการออกข้อผิดพลาดหากสิ่งต่าง ๆ ไม่ได้ผลจริง ๆ บนคอนกรีต เรียก.

จริงๆ แล้วฉันต้องการปรับปรุงโมเดลทางจิตของฉันเกี่ยวกับวิธีการทำงานภายในเพื่อให้ได้วิธีแก้ปัญหาที่รวดเร็วขึ้น ใน kvasir::mpl เรากำลังทดลองกับการติดตามความสมบูรณ์ด้วยตนเองเบื้องหลังเพื่อกำจัดการโทรที่ต้องพึ่งพาซึ่งทำ ทำให้สิ่งต่าง ๆ ช้าลงเล็กน้อย

person odinthenerd    schedule 22.11.2017
comment
ขอบคุณ คุณสามารถคัดลอกรหัสวิธีแก้ปัญหาของคุณไปยังคำตอบของคุณได้หรือไม่? - person Constructor; 22.11.2017

ในฐานะ @T.C. ระบุไว้ในความคิดเห็นต่อคำถาม รหัสดังกล่าวมีรูปแบบไม่ถูกต้อง (ไม่จำเป็นต้องวินิจฉัย)

มาตรฐาน C++14 ส่วน "การจำแนกชื่อ" [temp.res] ย่อหน้าที่ 8:

หากความเชี่ยวชาญเฉพาะทางที่ถูกต้องทุกประการของเทมเพลต variadic จำเป็นต้องมีแพ็กพารามิเตอร์เทมเพลตเปล่า เทมเพลตนั้นมีรูปแบบที่ไม่ถูกต้อง และไม่จำเป็นต้องทำการวินิจฉัย

ร่างล่าสุดของมาตรฐาน C++ ส่วน "การจำแนกชื่อ" [temp.res] ย่อหน้าที่ 8.3:

...โปรแกรมมีรูปแบบไม่ถูกต้อง ไม่จำเป็นต้องทำการวินิจฉัย หาก:

  • ...
  • ทุกความเชี่ยวชาญที่ถูกต้องของเทมเพลต variadic จำเป็นต้องมีแพ็กพารามิเตอร์เทมเพลตเปล่า...

ข้อมูลเพิ่มเติม: ประเด็นหลักปี 2067.

ตามข้อกำหนดมาตรฐาน วิธีแก้ปัญหาง่ายๆ ดังกล่าวสามารถเขียนได้:

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

การสาธิตสด

อัปเดต: ในฐานะ @odinthenerd ระบุไว้ในความคิดเห็น วิธีแก้ปัญหานี้ใช้ประเภทเพิ่มเติมที่ ส่งผลให้การคอมไพล์โปรแกรมช้าลง

person Constructor    schedule 22.11.2017
comment
ฉันคิดว่านี่เป็นคำตอบที่ถูกต้องจริง ๆ และวิธีแก้ปัญหาของฉันก็ใช้งานได้ ควรสังเกตว่าวิธีแก้ปัญหาชั่วคราวจะสร้างประเภทและจะช้าลง - person odinthenerd; 27.11.2017
comment
@odinthenerd ฉันได้เพิ่มบันทึกที่จำเป็นแล้ว - person Constructor; 29.11.2017
comment
@odinthenerd คุณรังเกียจไหมที่ฉันยอมรับคำตอบของฉันแทนที่จะเป็นของคุณ? - person Constructor; 29.11.2017