🗜️

[便利機能紹介] std::clamp

2024/10/16に公開

特定の値valueを範囲内[min_value, max_value]に収めたい時には、以下のようなコードを使うと思います。

const int clamped_value(std::min(std::max(value, min_value), max_value));

C++17 からようやくstd::clampという関数が追加されました!

以下のように使います。

#include <algorithm>

const int clamped_value(std:clamp(value, min_value, max_value));

valuemin_valueより小さい場合はmin_valueを、valuemax_valueより大きい場合はmax_valueを、そうでなければvalueを返します。

唯一注意しないといけないのは、この関数はmin_valuemax_valueより小さいか同等という前提で動いています。min_valuemax_valueより大きい場合は、エラーが返されずに未定義動作になります。つまり、コンパイラーの実装によって戻り値が変わります。

以下のように様々な実装が考えられますが、valueをどの順番でmin_valuemax_valueと比較するかによって、結果が変わります。実際はテンプレート関数になっていると思いますが、ここではシンプルにするためにint型に固定します。

int clamp1(int value, int min_value, int max_value)
{
    return std::max(std::min(value, max_value), min_value);
}

int clamp2(int value, int min_value, int max_value)
{
    return std::min(std::max(value, min_value), max_value);
}

int clamp3(int value, int min_value, int max_value)
{
    return value < min_value ? min_value : max_value < value ? max_value : value;
}

int clamp4(int value, int min_value, int max_value)
{
    return max_value < value ? max_value : value < min_value ? min_value : value;
}

std::cout << clamp1(0, 1, -1) << std::endl; // 1
std::cout << clamp2(0, 1, -1) << std::endl; // -1
std::cout << clamp3(0, 1, -1) << std::endl; // 1
std::cout << clamp4(0, 1, -1) << std::endl; // -1

実際にオンラインコンパイラーを使って、色々なコンパイラーでstd::clampでも同じケースを試してみた結果、「-1」が返されました。

std::cout << std::clamp(0, 1, -1) << std::endl; // -1

どちらにしても、予想外の値が返されるため、std::clampを使う前に、以下のようにmax_valuemin_valueより大きいかをチェックしてください。

#include <algorithm>
#include <cassert>

assert(min_value <= max_value); // min_value > max_value の場合はここでエラーになります。
const int clamped_value(std::clamp(value, min_value, max_value));

是非使ってみてください!


|cpp記事一覧へのリンク|

Discussion