SFINAE ใช้ VoidT กับคอมไพเลอร์ต่าง ๆ นำไปสู่ผลลัพธ์ที่ต่างกัน


10

พิจารณารหัสต่อไปนี้:

template <typename T> using VoidT = void;

class A {
public:
   using TEST = int;
};

class C {
public:
   using DIFFERENT = int;
};

template <typename T, typename Enable = void>
class B {
public:
   B() = delete;
};

template <typename T>
class B<T, VoidT<typename T::TEST>> {
public:
   B() = default;
};

template <typename T>
class B<T, VoidT<typename T::DIFFERENT>> {
public:
   B() = default;
};

int main() {
   B<A> a;
   B<C> b;

   return 0;
}

ใช้ g ++ - 4.8.5 การคอมไพล์โค้ดนี้ให้ข้อความแสดงข้อผิดพลาดต่อไปนี้ให้ฉัน:

~/test/compile_test> g++ -std=c++11 test.cpp

test.cpp:31:7: error: redefinition of ‘class B<T, void>’

test.cpp:24:7: error: previous definition of ‘class B<T, void>’

อย่างไรก็ตามเมื่อฉันคอมไพล์โดยใช้ g ++ - 8.3 (ใน, เช่น, ideone) คอมไพล์โค้ดและความเชี่ยวชาญต่าง ๆ ได้รับการปฏิบัติอย่างถูกต้อง นี่เป็นข้อบกพร่องใน GCC ที่ได้รับการแก้ไขหรือฉันกำลังเรียกใช้พฤติกรรมที่ไม่ได้กำหนดอย่างใดอย่างหนึ่ง (และดังนั้นความแตกต่างในพฤติกรรมของคอมไพเลอร์คือจุดที่สงสัย - มันไม่ได้กำหนด)?

คำตอบ:


9

นี่เป็นข้อบกพร่องใน GCC ที่ได้รับการแก้ไขหรือไม่

มันเป็นข้อบกพร่องในมาตรฐาน มันได้รับการแก้ไขย้อนหลังสำหรับเวอร์ชันมาตรฐานก่อนหน้านี้ แต่แน่นอนว่าเฉพาะเวอร์ชันคอมไพเลอร์ที่ใหม่กว่าเท่านั้นที่จะมีการแก้ไข มันคือCWG Issue 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 เป็นค่าเทียบเท่ากับโมฆะหรือเป็นความล้มเหลวในการทดแทนหรือไม่?

วิธีแก้ปัญหาสำหรับคอมไพเลอร์ที่ไม่มีการแก้ไข DR คือการใช้ตัวช่วย:

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

รับประกันความล้มเหลวในการทดแทนในแม่แบบคลาส


1
การแก้ไขย้อนหลังรบกวนฉัน มันหมายความว่าไม่เคยมีเอกสารมาตรฐานอธิบายถึงภาษาใด ๆ
การแข่งขัน Lightness ใน Orbit

2
@LightnessRacesinOrbit - ฉันเห็นประเด็นของคุณ ใคร ๆ ก็สามารถหวังได้ว่าการแก้ไขย้อนหลังนั้นสงวนไว้สำหรับสิ่งก่อสร้างที่ถูกต้องเท่านั้นซึ่งไม่ควรปฏิเสธดังนั้นความเสียหายจึงมีน้อยที่สุด
StoryTeller - Unslander Monica

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