From 2595bf57b318991a6d70ef1eea8d60b87f637fd3 Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Wed, 10 Jan 2024 19:17:28 +0000 Subject: [PATCH] Fix formatting of ranges with begin()&/end()& C++20 allows ranges to have lvalue-qualified begin() and end() member functions. fmt correctly handles this if begin() and end() are additionally const-qualifed (i.e. begin() const&), but not in the non-const case. For example: https://godbolt.org/z/YfxaYz5r7 This patch fixes fmt's range detection to handle this case by testing calls to detail::ranges_begin()/end() with an lvalue T&, matching the behaviour in the const case. --- include/fmt/ranges.h | 4 ++-- test/ranges-test.cc | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 75666d1cd755..8f8971c38551 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -132,8 +132,8 @@ struct has_const_begin_end< template struct has_mutable_begin_end< - T, void_t())), - decltype(detail::range_end(std::declval())), + T, void_t())), + decltype(detail::range_end(std::declval())), // the extra int here is because older versions of MSVC don't // SFINAE properly unless there are distinct types int>> : std::true_type {}; diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 2d7551292142..4eafcbe1f7e3 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -592,3 +592,14 @@ auto format_as(const tieable& t) -> std::tuple { TEST(ranges_test, format_as_tie) { EXPECT_EQ(fmt::format("{}", tieable()), "(3, 0.42)"); } + +struct lvalue_qualified_begin_end { + int arr[5] = {1, 2, 3, 4, 5}; + + int const* begin() & { return arr; } + int const* end() & { return arr + 5; } +}; + +TEST(ranges_test, lvalue_qualified_begin_end) { + EXPECT_EQ(fmt::format("{}", lvalue_qualified_begin_end{}), "[1, 2, 3, 4, 5]"); +}