Есть ли простой способ для создания итерируемого объекта, который будет вести себя
как правильный цикл для float
s?
Самый простой хак † будет использовать черты std::is_floating_point
, чтобы обеспечить различный возврат (т. Е. iter <= end
) в пределах operator!=
перегрузка.
( см. В прямом эфире )
#include <type_traits>
bool operator!=(const iterator& other)
{
if constexpr (std::is_floating_point_v<T>) return current <= other.current;
return !(*this == other);
}
† Предупреждение. Несмотря на то, что это делает свою работу, оно нарушает значение operator!=
перегрузка .
Альтернативное решение
Весь класс range
может быть заменен простой функцией, в которой значения диапазона будут заполняться с помощью std::iota
в стандартном контейнере std::vector
.
Используйте SFINE , чтобы ограничить использование функции только для допустимых типов.
Таким образом, вы можете положиться на стандартные реализации и забыть о переизобретениях.
( см. В прямом эфире )
#include <iostream>
#include <type_traits>
#include <vector> // std::vector
#include <numeric> // std::iota
#include <cstddef> // std::size_t
#include <cmath> // std::modf
// traits for valid template types(integers and floating points)
template<typename Type>
using is_integers_and_floats = std::conjunction<
std::is_arithmetic<Type>,
std::negation<std::is_same<Type, bool>>,
std::negation<std::is_same<Type, char>>,
std::negation<std::is_same<Type, char16_t>>,
std::negation<std::is_same<Type, char32_t>>,
std::negation<std::is_same<Type, wchar_t>>
/*, std::negation<std::is_same<char8_t, Type>> */ // since C++20
>;
template <typename T>
auto ragesof(const T begin, const T end)
-> std::enable_if_t<is_integers_and_floats<T>::value, std::vector<T>>
{
if (begin >= end) return std::vector<T>{}; // edge case to be considered
// find the number of elements between the range
const std::size_t size = [begin, end]() -> std::size_t
{
const std::size_t diffWhole
= static_cast<std::size_t>(end) - static_cast<std::size_t>(begin);
if constexpr (std::is_floating_point_v<T>) {
double whole; // get the decimal parts of begin and end
const double decimalBegin = std::modf(static_cast<double>(begin), &whole);
const double decimalEnd = std::modf(static_cast<double>(end), &whole);
return decimalBegin <= decimalEnd ? diffWhole + 1 : diffWhole;
}
return diffWhole;
}();
// construct and initialize the `std::vector` with size
std::vector<T> vec(size);
// populates the range from [first, end)
std::iota(std::begin(vec), std::end(vec), begin);
return vec;
}
int main()
{
for (auto i : ragesof( 5, 9 ))
std::cout << i << ' '; // prints 5 6 7 8
std::cout << '\n';
for (auto i : ragesof(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
}