Clang no compila código, pero gcc y msvc lo compilaron

14

No entiendo cuál es el problema: ya sea en mi código o en el compilador (menos posible). Hay un código como este:

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

Tanto GCC como MSVC lo compilan. Lo probé en godbolt con diferentes versiones de GCC y MSVC 17 (local) y 19. Aquí hay un enlace: https://godbolt.org/z/Enfm6L .

Pero Clang no lo compila y emite un error:

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

Y estoy interesado, tal vez hay alguna parte del estándar en la que este código es incorrecto o tal vez algo más.

Andrei
fuente
¿Cómo se definen "std :: set :: reverse_iterator" y "std :: set :: dummy_iterator" en los encabezados de clang?
mvidelgauz
std :: set :: dummy_iterator no está definido en los encabezados de clang (espero). Puede cambiar dummy_iterator a cualquier cosa que desee y no cambiará el resultado ya que el problema no está en la definición como se ve a continuación.
Andrei
Gracias Andrei, leí la respuesta y es realmente interesante
mvidelgauz

Respuestas:

9

Es muy probable que esto esté relacionado con CWG 1558 .

El tratamiento de los argumentos no utilizados en una especialización de plantilla de alias no está especificado por la redacción actual de 17.6.7 [temp.alias]. Por ejemplo:

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

¿La referencia a first_of con T es int equivalente a simplemente vacío, o es una falla de sustitución?

Es un defecto que se abordó desde entonces, pero si la versión de Clang que usó aún no implementa la solución, aún puede considerar ambas especializaciones simplemente definiendo el segundo argumento como void, y no haciendo toda la división de fallas de sustitución. La solución alternativa es no usar un alias simple std::void_t, sino una versión un poco más compleja

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

Para una plantilla de clase (que es lo que ahora representa el alias), se define el error de sustitución. Conectar eso a su ejemplo apacigua Clang https://godbolt.org/z/VnkwsM .

StoryTeller - Unslander Monica
fuente
1
Otra solución sería crear rasgos para cada requisito y luego combinarlos en una cláusula enable_ifcon std::disjunction(o una cláusula
obligatoria
Gracias por la ayuda y referencia! Es triste escuchar sobre este tipo de error en Clang. Sin embargo, es curioso, pensé que tu implementación de void_t es estándar. No se puede adoptar la idea de alias de plantilla.
Andrei