https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79960
--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> --- It's possible this isn't a bug, and the partial specializations need to be constrained to avoid the ambiguity (although both Clang trunk and EDG accept the unconstrained cases, but Clang 3.x fails similarly to G++). This works with any G++ version: using size_t = decltype(sizeof(0)); struct true_type { static constexpr bool value = true; }; struct false_type { static constexpr bool value = false; }; template<bool> struct enable_if { }; template<> struct enable_if<true> { using type = void; }; template<typename T> using disable_if = typename enable_if<!T::value>::type; template<typename T> struct is_const : false_type { }; template<typename T> struct is_const<const T> : true_type { }; template<typename T> struct is_volatile : false_type { }; template<typename T> struct is_volatile<volatile T> : true_type { }; template<typename T> struct tuple_size; template<typename T, typename = void, size_t U = tuple_size<T>::value> using __has_tuple_size = T; template<typename T> struct tuple_size<const __has_tuple_size<T, disable_if<is_volatile<T>>>> { static constexpr size_t value = tuple_size<T>::value; }; template<typename T> struct tuple_size<volatile __has_tuple_size<T, disable_if<is_const<T>>>> { static constexpr size_t value = tuple_size<T>::value; }; template<typename T> struct tuple_size<const volatile __has_tuple_size<T>> { static constexpr size_t value = tuple_size<T>::value; }; template<typename... T> struct tuple { }; template<typename... T> struct tuple_size<tuple<T...>> { static constexpr size_t value = sizeof...(T); }; static_assert( tuple_size<const tuple<>>::value == 0, "" ); // OK static_assert( tuple_size<volatile tuple<>>::value == 0, "" ); // OK static_assert( tuple_size<const volatile tuple<>>::value == 0, "" ); // OK