On Wed, 16 Apr 2025 at 12:54, Tomasz Kamiński <tkami...@redhat.com> wrote: > > The _Rg is deduced to lvalue reference for the lvalue arguments, > and in such case __format::__maybe_const_range<_Rg, _CharT> is always _Rg > (adding const to reference does not change behavior). > > Now we correctly check if _Range = const remove_reference_t<_Rg> is > formattable range, furthermore as range_formatter<T> can only format > ranges of values of type (possibly const) _Tp, we additional check if the > remove_cvref_t<range_reference_t<const _Range>> is _Tp. > > The range_reference_t<T> and range_reference_t<const T> have different > type (module remove_cvref_t) for std::vector<bool> (::reference and bool)
s/module/modulo/ ? OK with that tweak. > or flat_map<T, U> (pair<const T&, U&> and pair<const T&, const U&>). > > PR libstdc++/PR109162 > > libstdc++-v3/ChangeLog: > > * include/std/format (range_formatter::format): Format const range, > only if reference type is not changed. > * testsuite/std/format/ranges/formatter.cc: New tests. > --- > Testing on x86_64-linux, test for *format* passed. > OK for trunk? > > libstdc++-v3/include/std/format | 11 +++++++--- > .../testsuite/std/format/ranges/formatter.cc | 22 +++++++++++++++++++ > 2 files changed, 30 insertions(+), 3 deletions(-) > > diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format > index b1455977c65..3ae1fc1877c 100644 > --- a/libstdc++-v3/include/std/format > +++ b/libstdc++-v3/include/std/format > @@ -5634,9 +5634,14 @@ namespace __format > typename basic_format_context<_Out, _CharT>::iterator > format(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const > { > - using __maybe_const_range > - = __format::__maybe_const_range<_Rg, _CharT>; > - return _M_format<__maybe_const_range>(__rg, __fc); > + using _Range = remove_reference<_Rg>; > + if constexpr (__format::__const_formattable_range<_Range, _CharT>) > + { > + using _CRef = ranges::range_reference_t<const _Range>; > + if constexpr (same_as<remove_cvref_t<_CRef>, _Tp>) > + return _M_format<const _Range>(__rg, __fc); > + } > + return _M_format(__rg, __fc); > } > > private: > diff --git a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc > b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc > index a4f5d9210dd..00ce9f6dd0c 100644 > --- a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc > +++ b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc > @@ -1,5 +1,6 @@ > // { dg-do run { target c++23 } } > > +#include <flat_map> > #include <format> > #include <testsuite_hooks.h> > #include <vector> > @@ -138,6 +139,26 @@ test_nested() > VERIFY( res == "+<01; 02; 11; 12>+" ); > } > > +struct MyFlatMap : std::flat_map<int, int> > +{ > + using std::flat_map<int, int>::flat_map; > +}; > + > +template<typename CharT> > +struct std::formatter<MyFlatMap, CharT> > + // This cannot apply format BitVector const&, because formatted type would > + // be std::pair<int const&, int const&>, and formatter for > + // pair<int const&, int> cannot format it. > + : std::range_formatter<MyFlatMap::reference> > +{}; > + > +void test_const_ref_type_mismatch() > +{ > + MyFlatMap m{{1, 11}, {2, 22}}; > + std::string res = std::format("{:m}", m); > + VERIFY( res == "{1: 11, 2: 22}" ); > +} > + > template<typename T, typename CharT> > using VectorFormatter = std::formatter<std::vector<T>, CharT>; > > @@ -146,4 +167,5 @@ int main() > test_outputs<std::range_formatter>(); > test_outputs<VectorFormatter>(); > test_nested(); > + test_const_ref_type_mismatch(); > } > -- > 2.49.0 >