QRM: make size() work for ranges that don't have begin/end(const R&)

Use const_cast to call the mutable overload if we cannot call the const
overload. This allows us to directly support e.g. std::views::filter.
As a drive by, use using-statements to reduce code duplication.

Use std::views::filter directly in the tests, now that it works.

Change-Id: I47f06ee8fe921d5854f676a35a750e64f4356fc0
Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
This commit is contained in:
Volker Hilsheimer 2025-08-30 11:52:04 +02:00
parent a113197fec
commit 6784a7aff7
3 changed files with 22 additions and 9 deletions

View File

@ -480,6 +480,13 @@ namespace QRangeModelDetails
template <typename C>
struct test_size<C, std::void_t<decltype(std::size(std::declval<C&>()))>> : std::true_type {};
template <typename C, typename = void>
struct test_cbegin : std::false_type {};
template <typename C>
struct test_cbegin<C, std::void_t<decltype(std::begin(std::declval<const C&>()))>>
: std::true_type
{};
template <typename C, typename = void>
struct range_traits : std::false_type {
static constexpr bool is_mutable = !std::is_const_v<C>;
@ -488,6 +495,7 @@ namespace QRangeModelDetails
static constexpr bool has_erase = false;
static constexpr bool has_resize = false;
static constexpr bool has_rotate = false;
static constexpr bool has_cbegin = false;
};
template <typename C>
struct range_traits<C, std::void_t<decltype(begin(std::declval<C&>())),
@ -503,6 +511,7 @@ namespace QRangeModelDetails
static constexpr bool has_erase = test_erase<C>();
static constexpr bool has_resize = test_resize<C>();
static constexpr bool has_rotate = test_rotate<iterator>();
static constexpr bool has_cbegin = test_cbegin<C>::value;
};
// Specializations for types that look like ranges, but should be
@ -515,6 +524,7 @@ namespace QRangeModelDetails
static constexpr bool has_erase = false;
static constexpr bool has_resize = false;
static constexpr bool has_rotate = false;
static constexpr bool has_cbegin = true;
};
template <> struct range_traits<QByteArray> : iterable_value<Mutable::Yes> {};
template <> struct range_traits<QString> : iterable_value<Mutable::Yes> {};
@ -1042,19 +1052,23 @@ protected:
template <typename C>
static constexpr int size(const C &c)
{
if (!QRangeModelDetails::isValid(c))
using namespace QRangeModelDetails;
if (!isValid(c))
return 0;
if constexpr (QRangeModelDetails::test_size<C>()) {
if constexpr (test_size<C>()) {
return int(std::size(c));
} else {
#if defined(__cpp_lib_ranges)
return int(std::ranges::distance(QRangeModelDetails::begin(c),
QRangeModelDetails::end(c)));
using std::ranges::distance;
#else
return int(std::distance(QRangeModelDetails::begin(c),
QRangeModelDetails::end(c)));
using std::distance;
#endif
using container_type = std::conditional_t<range_traits<C>::has_cbegin,
const wrapped_t<C>,
wrapped_t<C>>;
container_type& container = const_cast<container_type &>(refTo(c));
return int(distance(std::begin(container), std::end(container)));
}
}

View File

@ -1219,7 +1219,7 @@ void tst_QRangeModel::filterAsRange()
| std::views::filter([](int i){ return 0 == i % 2; })
| std::views::transform([](int i){ return i * i; });
QRangeModel model(std::ranges::subrange(view.begin(), view.end()));
QRangeModel model(view);
QCOMPARE(model.rowCount(), 50);
#else
QSKIP("Test of std::ranges requires C++ 20");

View File

@ -294,8 +294,7 @@ public slots:
| std::views::filter([](int i){ return 0 == i % 2; })
| std::views::transform([](int i){ return i * i; });
// suboptimal: eager evaluation of view.begin()
return new QRangeModel(std::ranges::subrange(view.begin(), view.end()));
return new QRangeModel(view);
}
QRangeModel *makeMultiRoleMap()