Clang không biên dịch mã nhưng gcc và msvc đã biên dịch nó


14

Tôi không hiểu vấn đề là gì: trong mã của tôi hoặc trong trình biên dịch (ít có thể hơn). Có một đoạn mã như thế này:

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

Cả GCC và MSVC đều biên dịch nó. Tôi đã thử nghiệm nó trên godbolt với phiên bản khác nhau của GCC và MSVC 17 (cục bộ) và 19. Đây là một liên kết: https://godbolt.org/z/Enfm6L .

Nhưng Clang không biên dịch nó và phát ra lỗi:

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

Và tôi quan tâm - có thể có một phần của tiêu chuẩn nơi đoạn mã này không chính xác hoặc có thể là thứ gì đó khác.


Làm thế nào là "std :: set :: reverse_iterator""std :: set :: dummy_iterator" được định nghĩa trong tiêu đề kêu vang?
mvidelgauz

std :: set :: dummy_iterator hoàn toàn không được định nghĩa trong các tiêu đề clang (tôi hy vọng). Bạn có thể thay đổi dummy_iterator thành bất kỳ thứ gì bạn muốn và nó sẽ không thay đổi kết quả vì vấn đề không nằm trong định nghĩa như dưới đây.
Andrei

Cảm ơn Andrei, tôi đã đọc câu trả lời và thực sự rất thú vị
mvidelgauz

Câu trả lời:


9

Điều này rất có thể liên quan đến CWG 1558 .

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ế?

Đó là một khiếm khuyết đã được xử lý, nhưng nếu phiên bản Clang bạn đã sử dụng chưa thực hiện sửa lỗi, thì nó vẫn có thể coi cả hai chuyên môn chỉ đơn giản là xác định đối số thứ hai là voidvà không thực hiện toàn bộ lỗi thay thế. Cách giải quyết là không sử dụng bí danh đơn giản std::void_tmà thay vào đó là phiên bản phức tạp hơn một chút

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

Đối với một mẫu lớp (đó là những gì bí danh hiện nay), lỗi thay thế được xác định. Việc cắm nó vào ví dụ của bạn sẽ làm hài lòng Clang https://godbolt.org/z/VnkwsM .


1
Cách giải quyết khác là tạo ra những đặc điểm cho mỗi yêu cầu, và sau đó kết hợp chúng trong một enable_ifvới std::disjunction(hoặc một đòi hỏi khoản)
AndyG

Cảm ơn bạn đã giúp đỡ và tham khảo! Thật buồn khi nghe về loại lỗi này ở Clang. Mặc dù vậy, thật buồn cười, tôi nghĩ rằng việc triển khai void_t của bạn là chuẩn. Không thể áp dụng ý tưởng răng cưa mẫu.
Andrei
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.