เสียงดังกราวไม่ได้รวบรวมรหัส แต่ gcc และ msvc รวบรวมไว้


14

ฉันไม่เข้าใจว่ามีปัญหาอะไร: ในโค้ดของฉันหรือในคอมไพเลอร์ (น้อยกว่านั้นเป็นไปได้) มีชิ้นส่วนของรหัสเช่นนี้:

#include <iostream>
#include <type_traits>
#include <set>


template<typename T, typename = void>
struct TestA: std::false_type {};

template<typename T>
struct TestA<T, std::void_t<typename T::reverse_iterator>> : std::true_type {};

template<typename T>
struct TestA<T, std::void_t<typename T::dummy_iterator>> : std::true_type {};

int main()
{
    std::cout << TestA<std::set<int>>::value;
}

ทั้ง GCC และ MSVC รวบรวมมัน ผมทดสอบบน godbolt กับรุ่นที่แตกต่างกันของ GCC และ MSVC 17 (ท้องถิ่น) และ 19. นี่คือลิงค์: https://godbolt.org/z/Enfm6L

แต่เสียงดังกราวไม่ได้รวบรวมและส่งข้อผิดพลาด:

redefinition of `'TestA<T, std::void_t<typename T::dummy_iterator> >'`

และฉันสนใจ - อาจมีมาตรฐานบางส่วนที่โค้ดนี้ไม่ถูกต้องหรืออาจเป็นอย่างอื่น


เป็นอย่างไรบ้าง"มาตรฐาน :: ชุด :: reverse_iterator"และ"มาตรฐาน :: ตั้ง :: dummy_iterator"ที่กำหนดไว้ในส่วนหัวของเสียงดังกราว?
mvidelgauz

std :: set :: dummy_iterator ไม่ได้กำหนดไว้ในส่วนหัวของเสียงดังกราวเลย (หวังว่า) คุณสามารถเปลี่ยน dummy_iterator เป็นสิ่งที่คุณต้องการและจะไม่เปลี่ยนผลลัพธ์เนื่องจากปัญหาไม่ได้อยู่ในคำนิยามตามที่เห็นด้านล่าง
Andrei

ขอบคุณ Andrei ฉันอ่านคำตอบและมันน่าสนใจจริงๆ
mvidelgauz

คำตอบ:


9

นี้จะมีโอกาสมากที่เกี่ยวข้องกับการCWG 1558

การรักษาข้อโต้แย้งที่ไม่ได้ใช้ในความเชี่ยวชาญเทมเพลตนามแฝงไม่ได้ถูกระบุโดยถ้อยคำปัจจุบันของ 17.6.7 [temp.alias] ตัวอย่างเช่น:

  #include <iostream>

  template <class T, class...>
    using first_of = T;

  template <class T>
    first_of<void, typename T::type> f(int)
      { std::cout << "1\n"; }

  template <class T>
    void f(...)
      { std::cout << "2\n"; }

  struct X { typedef void type; };

  int main() {
    f<X>(0);
    f<int>(0);
  }

การอ้างอิงถึง first_of โดยที่ T เป็นค่าเทียบเท่ากับโมฆะหรือเป็นความล้มเหลวในการทดแทนหรือไม่?

เป็นข้อบกพร่องที่ได้รับการแก้ไข แต่ถ้าเวอร์ชันของ Clang ที่คุณใช้ยังไม่ได้ใช้การแก้ไขมันอาจยังคงพิจารณาถึงความเชี่ยวชาญทั้งสองเป็นเพียงแค่การกำหนดอาร์กิวเมนต์ที่สองเป็นvoidและไม่ได้ทำเกมแทนที่ล้มเหลวทั้งหมด วิธีแก้ปัญหาคือไม่ใช้นามแฝงธรรมดาstd::void_tแต่แทนที่จะเป็นเวอร์ชันที่ซับซ้อนกว่าเล็กน้อย

template <typename...> struct voider { using type = void; };
template <typename... T> using my_void_t = typename voider<T...>::type;

สำหรับเทมเพลตชั้นเรียน (ซึ่งเป็นสิ่งที่นามแฝงหมายถึงตอนนี้) ความล้มเหลวในการแทนที่ถูกกำหนดไว้ เสียบที่เป็นตัวอย่างของคุณ appeases เสียงดังกราวhttps://godbolt.org/z/VnkwsM


1
วิธีแก้ปัญหาอื่นคือการสร้างลักษณะสำหรับแต่ละข้อกำหนดจากนั้นรวมเข้าenable_ifด้วยstd::disjunction(หรือประโยคที่ต้องใช้)
AndyG

ขอบคุณสำหรับความช่วยเหลือและการอ้างอิง! เสียใจที่ได้ยินเกี่ยวกับบั๊กชนิดนี้ใน Clang ตลก แต่ฉันคิดว่าการใช้ void_t ของคุณเป็นมาตรฐาน ไม่สามารถนำความคิดของการใช้นามแฝงแม่แบบ
Andrei
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.