SFINAE sử dụng VoidT với các trình biên dịch khác nhau dẫn đến kết quả khác nhau


10

Hãy xem xét các mã sau đây:

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

Sử dụng g ++ - 4.8.5, biên dịch mã này cho tôi thông báo lỗi sau:

~/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>’

Tuy nhiên, khi tôi biên dịch bằng g ++ - 8.3 (ví dụ: ideone), mã biên dịch và các chuyên môn khác nhau được xử lý chính xác. Đây có phải là một lỗi trong GCC đã được sửa hay tôi bằng cách nào đó gọi hành vi không xác định (và do đó, sự khác biệt trong hành vi của trình biên dịch là một điểm cần thiết - nó không được xác định)?

Câu trả lời:


9

Đây có phải là một lỗi trong GCC đã được sửa chữa?

Đó là một khiếm khuyết trong tiêu chuẩn. Nó đã được sửa lỗi hồi tố cho các phiên bản tiêu chuẩn trước đó, nhưng tất nhiên chỉ có các phiên bản trình biên dịch mới hơn mới có bản sửa lỗi. Đó là vấn đề CWG 1558 và trích dẫn từ đó:

Việc xử lý các đối số không được sử dụng trong chuyên môn mẫu bí danh không được chỉ định bởi từ ngữ hiện tại của 17.6.7 [temp.alias]. Ví dụ:

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

Là tham chiếu đến first_of với T là int tương đương với void, hay nó là một sự thay thế?

Cách giải quyết cho các trình biên dịch không có sửa lỗi DR là sử dụng một trình trợ giúp:

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

Thất bại thay thế được đảm bảo trong một mẫu lớp.


1
Sửa lỗi hồi tố làm phiền tôi. Nó có nghĩa là không bao giờ có một tài liệu kinh điển mô tả bất kỳ phiên bản nào của ngôn ngữ.
Các cuộc đua nhẹ nhàng trong quỹ đạo

2
@LightnessRacesinOrbit - Tôi thấy quan điểm của bạn. Người ta có thể hy vọng rằng các bản sửa lỗi hồi tố như vậy chỉ được dành riêng cho các cấu trúc hợp lệ không nên bị từ chối, vì vậy tác hại là rất nhỏ.
Người kể chuyện - Unslander Monica

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.