Clang не компілює код, але 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> >'`

І мені цікаво - можливо, є якась частина стандарту, де цей фрагмент коду невірний, чи, можливо, щось інше.


Як визначаються "std :: set :: reverse_iterator" та "std :: set :: dummy_iterator" у заголовках clang ?
mvidelgauz

std :: set :: dummy_iterator взагалі не визначено у заголовках clang (сподіваюся). Ви можете змінити dummy_iterator на будь-який, що хочете, і це не змінить результат, оскільки проблема не полягає у визначенні, як показано нижче.
Андрій

Дякую, Андрію, я прочитав відповідь, і це справді цікаво
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 є int еквівалентним просто недійсним, або це збій заміни?

Це дефект, з якого було звернено увагу, але якщо використана вами версія Clang ще не реалізує виправлення, вона все ще може розглядати обидві спеціалізації як просто визначення другого аргументу як void, а також не виконувати весь провал відмови заміни. Вирішення проблеми полягає в тому, щоб не використовувати звичайний псевдонім std::void_t, а замість цього трохи складнішу версію

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

Для шаблону класу (який зараз означає псевдонім) визначається збій підстановки. Підключення цього до вашого прикладу використовує Clang https://godbolt.org/z/VnkwsM .


1
Інше рішення було б створити риси для кожного вимоги, а потім об'єднати їх у enable_ifс std::disjunction(або вимагає пункт)
AndyG

Дякую за допомогу та довідку! Сумно почути про подібний клоп у Кланг. Хоча смішно, я вважав, що ваша реалізація void_t є стандартною. Неможливо прийняти ідею псевдоніму шаблону.
Андрій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.