min_element - stl c++ max




어떻게`range`와 같은 반복 가능한 float 객체를 만드는가? (3)

float 의 올바른 for 루프처럼 작동하는 반복 가능한 객체를 만드는 간단한 방법 이 있습니까?

가장 단순한 해킹 std::is_floating_point 라는 특성을 사용하여 operator!= 내에서 다른 리턴 (예 : iter <= end )을 제공합니다 operator!= overload.

( 라이브보기 )

#include <type_traits>

bool operator!=(const iterator& other)
{
    if constexpr (std::is_floating_point_v<T>) return current <= other.current;
    return !(*this == other);
}

경고 : 그 일을하더라도, 그것은 operator!= 의 의미를 깨뜨 립니다 operator!= overload .

대체 솔루션

전체 range 클래스는 range 의 값이 표준 컨테이너 std::vector 에있는 std::iota 의 도움으로 채워지는 간단한 함수로 대체 될 수 있습니다.

유효한 유형에 대해서만 함수의 사용을 제한하려면 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
}

C ++ 에서 range 와 유사한 구문을 만들고 싶습니다. 다음과 같이 사용합니다.

for (auto i: range(5,9))
    cout << i << ' ';    // prints 5 6 7 8 

for (auto i: range(5.1,9.2))
    cout << i << ' ';    // prints 5.1 6.1 7.1 8.1 9.1

정수의 경우를 처리하는 것은 비교적 쉽습니다.

template<typename T>
struct range 
{
    T from, to;
    range(T from, T to) : from(from), to(to) {}

    struct iterator
    {
        T current;
        T operator*() {  return current; }

        iterator& operator++()
        {
            ++current;
            return *this;
        }

        bool operator==(const iterator& other) { return current == other.current; }
        bool operator!=(const iterator& other) { return current != other.current; }
    };

    iterator begin() const { return iterator{ from }; }
    iterator end()   const { return iterator{ to }; }
};

그러나 C++ 의 표준 범위 기반 루프는 iter==end 인지 여부를 확인하고 for 루프 에서처럼 iter <= end 가 아닌지 여부를 확인하기 때문에 float 경우에는 작동하지 않습니다.

float 의 올바른 for 루프처럼 작동하는 반복 가능한 객체를 만드는 간단한 방법이 있습니까?


반복자의 의미를 방해하지 않는 시도입니다. 이제는 각 반복기가 정지 값을 알고 있으므로 초과 할 때마다 자체적으로 설정됩니다. 그러므로 동등한 범위의 모든 최종 반복자는 동등한 to 비교합니다.

template <typename T> 
struct range {
    T from, to;
    range(T from, T to): from(from), to(to) {}

    struct iterator {
        const T to; // iterator knows its bounds
        T current;

        T operator*() { return current; }

        iterator& operator++() { 
            ++current;
            if(current > to)
                // make it an end iterator
                // (current being exactly equal to 'current' of other end iterators)
                current = to;
            return *this;
        }

        bool operator==(const iterator& other) const // OT: note the const
        { return current == other.current; }
        // OT: this is how we do !=
        bool operator!=(const iterator& other) const { return !(*this == other); }
    };

    iterator begin() const { return iterator{to, from}; }
    iterator end()   const { return iterator{to, to}; }
};

이게 왜 더 좋은가요?

@JeJo의 솔루션은 반복자를 비교하는 순서에 의존합니다. 즉 it != end 또는 end != it 입니다. 그러나 범위 기반의 경우에는 정의됩니다 . 이기구를 다른 상황에서 사용해야한다면 위의 접근 방법을 권합니다.

또는 sizeof(T) > sizeof(void*) 인 경우 원본 range 인스턴스 (range-for의 경우 끝까지 지속 됨)에 대한 포인터를 저장하고이를 사용하여 단일 T 값:

template <typename T> 
struct range {
    T from, to;
    range(T from, T to): from(from), to(to) {}

    struct iterator {
        const range* range;
        T current;

        iterator& operator++() { 
            ++current;
            if(current > range->to)
                current = range->to;
            return *this;
        }

        ...
    };

    iterator begin() const { return iterator{this, from}; }
    iterator end()   const { return iterator{this, to}; }
};

또는 그것은 T const* const 일 수도 있고 그 값을 직접 가리킬 수도 있습니다. 그것은 당신에게 T const* const 습니다.

OT : 두 수업 모두에서 내부 용을 private 로 설정하는 것을 잊지 마십시오.


부동 소수점 루프 또는 이터레이터는 일반적으로 정수 유형을 사용하여 총 반복 횟수 및 현재 반복 횟수를 보유한 다음 루프 내에서 사용되는 "루프 인덱스"값을 루프 불변 부동 소수점을 기반으로 계산합니다 값.

예 :

for (int i=-10; i<=10; i++)
{
  double x = i/10.0;  // Substituting i*0.1 would be faster but less accurate
}

또는

for (int i=0; i<=16; i++)
{
  double x = ((startValue*(16-i))+(endValue*i))*(1/16);
}

반올림 오류가 반복 횟수에 영향을 줄 가능성은 없습니다. 후자의 계산은 끝점에서 올바르게 둥근 결과를 얻도록 보장됩니다. startValue+i*(endValue-startValue) 은 루프 불변 (endValue-startValue) 가 끌어 (endValue-startValue) 수 있기 때문에 더 빠를 것 같지만 정확도가 떨어질 수 있습니다.

정수를 부동 소수점 값으로 변환하는 함수와 함께 정수 반복기를 사용하는 것은 아마도 부동 소수점 값의 범위를 반복하는 가장 강력한 방법 일 것입니다. 부동 소수점 값을 직접 반복하려고하면 "off-by-one"오류가 발생할 확률이 훨씬 높아집니다.





range