From 79e48ba9156acb565b4aaa565b2ae2a58da798d2 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 20 Sep 2026 09:49:07 -0700 Subject: [PATCH] Move has_format_as to format.h and use it in the tuple formatter The tuple formatter had a bespoke trait to keep format_as from producing an ambiguous specialization. detail::has_format_as already answers that question and std.h applies it the same way, so move it from std.h to format.h where ranges.h can see it and drop the extra trait. Check it with conditional_t rather than &&: is_tuple_formattable must not be instantiated for types with format_as because for self-referential tuple-like types that recurses back into formatter selection, and && short-circuits evaluation but not instantiation. --- include/fmt/format.h | 5 +++++ include/fmt/ranges.h | 24 ++++++++++-------------- include/fmt/std.h | 6 ------ 3 files changed, 15 insertions(+), 20 deletions(-) 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