diff --git a/include/fmt/format.h b/include/fmt/format.h index 52b838f1..d52a6668 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4100,6 +4100,11 @@ template class nested_format_specs { ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } }; + +template +struct has_format_as : std::false_type {}; +template +struct has_format_as>> : std::true_type {}; } // namespace detail FMT_BEGIN_EXPORT diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index d7f66848..19a3e333 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -297,21 +297,17 @@ template struct is_tuple_formattable { static constexpr bool value = detail::is_tuple_formattable_::value; }; -namespace detail { -template -struct is_tuple_formattable_for_formatter - : std::integral_constant::value> {}; - -template -struct is_tuple_formattable_for_formatter>> - : std::false_type {}; -} // namespace detail - template -struct formatter::value && - detail::is_tuple_formattable_for_formatter< - Tuple, Char>::value>> { +struct formatter< + Tuple, Char, + // format_as takes precedence. conditional_t rather than && because + // is_tuple_formattable must not be instantiated for types with format_as: + // for self-referential tuple-like types it recurses back into this + // specialization. + enable_if_t< + fmt::is_tuple_like::value && + conditional_t::value, std::false_type, + fmt::is_tuple_formattable>::value>> { private: decltype(detail::tuple::get_formatters( detail::tuple_index_sequence())) formatters_; diff --git a/include/fmt/std.h b/include/fmt/std.h index cd1baccc..8a9a94d3 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -279,12 +279,6 @@ struct is_bit_reference_like> { }; #endif -template -struct has_format_as : std::false_type {}; -template -struct has_format_as()))>> - : std::true_type {}; - template struct has_format_as_member : std::false_type {}; template