클램프 기능이 잘못 설계됨
15858 단어 cprogrammingdebuggingcpp
std::clamp
및 std::ranges::clamp
함수가 잘못 설계된 이유와 간단히 개선할 수 있는 방법을 알아보겠습니다.자기 홍보:
저를 팔로우하고 프로그래머이자 작가로서의 제 작업을 확인할 수 있는 몇 가지 소셜 네트워크가 있습니다 😉
Personal blog , , GitHub
std::clamp 및 std::ranges::clamp 관련 문제
C++17부터 함수std::clamp는 함수 호출 중에 지정된 2개의 변수로 정의된 값 범위에서 변수를 유지하는 도우미로 사용할 수 있습니다. C++20에서는 함수std::ranges::clamp도 C++ 표준에 추가되었습니다.
이러한 기능은 다음과 같이 정의됩니다.
namespace std
{
template<class T>
constexpr const T& clamp( const T& v, const T& lo, const T& hi );
template<class T, class Compare>
constexpr const T& clamp( const T& v, const T& lo, const T& hi, Compare comp );
namespace ranges
{
template< class T, class Proj = std::identity,
std::indirect_strict_weak_order<std::projected<const T*, Proj>> Comp =
ranges::less >
constexpr const T&
clamp( const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {} );
} // namespace ranges
} // namespace std
이러한 함수를 사용하는 코드는 다음과 같습니다.
auto value = std::clamp (variable, 0, 100);
auto other_value = std::ranges::clamp (other_variable, -100, 0);
그러나 다음 코드와 같이 보일 수도 있습니다.
auto value = std::clamp (50, 100, 75);
그리고 이 경우 이 코드 줄을 작성하는 개발자의 의도가 무엇인지 추측하기가 매우 어렵습니다.
실제로 범위의 최소값과 최대값은 코드의 실수로 인해 뒤바뀔 수 있습니다. 이는 C++ 표준에 따르면 정의되지 않은 동작입니다. 또는 이 행을 작성하는 개발자는
clamp
함수가 고정할 값 이전의 범위를 취하고 있다고 생각했습니다.이유의 원인이 무엇이든 이 코드는 컴파일 중에 경고를 생성하지 않습니다. 이러한 종류의 오류가 눈에 띄지 않게 하는 솔루션을 쉽게 상상할 수 있습니다.
개선 가능
개발자의 삶을 더 쉽게 만들기 위해 한 가지 해결책은
clamp
함수의 디자인을 변경하는 것입니다. 즉, clamp
범위와 관련된 2개의 매개변수를 결합하는 것입니다. 예를 들어 std::pair을 사용하여 결합할 수 있습니다.#include <cassert>
#include <functional>
namespace mystd
{
template<class T, class Compare>
constexpr const T& clamp( const T& v, const std::pair<T, T>& range, Compare comp )
{
assert(comp(range.first, range.second));
return comp(v, range.first) ? range.first : comp(range.second, v) ? range.second : v;
}
template<class T>
constexpr const T& clamp( const T& v, const std::pair<T, T>& range )
{
return clamp(v, range, std::less<T>{});
}
}
그런 식으로 함수
clamp
를 호출할 때 개발자는 고정할 값과 범위를 명확히 해야 합니다.auto v = mystd::clamp (5, {7, 10});
또한 범위의 최소값과 최대값이 반전되면 어설션이 트리거됩니다.
/app/example.cpp:10: constexpr const T& mystd::clamp(const T&, const std::pair<_FIter, _FIter>&, Compare) [with T = int; Compare = std::less<int>]: Assertion `comp(range.first, range.second)' failed.
std::pair
대신 구조를 사용하여 어설션을 좀 더 명시적으로 만들 수도 있습니다. 실제로 .first
및 .second
요소를 갖는 대신 min
및 max
특성을 명명했을 것입니다. 그렇게 하면 다음 코드가 됩니다.namespace mystd2
{
template<class T>
struct clamp_range
{
T min, max;
};
template<class T, class Compare>
constexpr const T& clamp( const T& v, const clamp_range<T>& range, Compare comp )
{
assert(comp(range.min, range.max));
return comp(v, range.min) ? range.min : comp(range.max, v) ? range.max : v;
}
template<class T>
constexpr const T& clamp( const T& v, const clamp_range<T>& range )
{
return clamp(v, range, std::less<T>{});
}
}
int main()
{
auto v = mystd2::clamp (5, {10, 7});
}
다음과 같은 주장을 할 것입니다.
/app/example.cpp:32: constexpr const T& mystd2::clamp(const T&, const mystd2::clamp_range<T>&, Compare) [with T = int; Compare = std::less<int>]: Assertion `comp(range.min, range.max)' failed.
, 개발자가 문제를 만났을 때 문제를 더 쉽게 디버깅할 수 있습니다.
결론
이 기사의 제목은 어떤 사람들에게는 약간의 클릭 미끼일 수 있으며, 다른 사람들은 여기에 설명된 솔루션이 아마도 개선될 수 있다고 말할 것입니다(예: 개념 포함). 그리고 이것은 아마도 사실일 것입니다! 그러나 결론은 동일하며 표준 기능
std::clamp
은 개발자가 피할 수 있는 실수를 피하도록 돕기 위해 개선될 수 있고 개선되어야 합니다.이 글에 설명된 솔루션을 실험해보고 싶다면 godbolt link 을 알려드리고,
std::clamp
기능을 더욱 개선할 수 있는 방법에 대한 아이디어가 있다면 이 글 아래에 약간의 의견을 자유롭게 적어주세요 😉이 글을 읽어주신 모든 분들께 감사드리며,
그리고 다음 글까지 화사한 하루 보내세요🙂
Reference
이 문제에 관하여(클램프 기능이 잘못 설계됨), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/10xlearner/the-clamp-functions-are-badly-designed-1dkn텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)