C++のstd::minについて

AI実装検定のご案内

std::minは、複数の値を比較し、その中から最も小さい値を取得するための標準ライブラリ関数です。

主に次のような用途で使用されます。

  • 2つの数値から小さい方を取得する
  • 複数の値から最小値を取得する
  • 独自の条件でオブジェクトを比較する
  • 値に上限を設定する
  • 配列やコンテナを処理する件数を制限する

std::minを使用する場合は、基本的に<algorithm>をインクルードします。

#include <algorithm>

基本的な使用例は次のとおりです。

#include <algorithm>
#include <iostream>

int main()
{
    int a = 10;
    int b = 20;

    int result = std::min(a, b);

    std::cout << result << '\n';
}

実行結果は次のようになります。

10
目次

std::minの基本構文

std::minには、主に次の4種類の形式があります。

template<class T>
constexpr const T& min(const T& a, const T& b);

template<class T, class Compare>
constexpr const T& min(const T& a, const T& b, Compare comp);

template<class T>
constexpr T min(std::initializer_list<T> values);

template<class T, class Compare>
constexpr T min(std::initializer_list<T> values, Compare comp);

それぞれの用途は次のとおりです。

書き方用途
std::min(a, b)2つの値を通常の大小関係で比較する
std::min(a, b, comp)独自の比較条件で2つの値を比較する
std::min({a, b, c})3つ以上の値から最小値を取得する
std::min({a, b, c}, comp)3つ以上の値を独自の条件で比較する

2つの値から最小値を取得する

整数を比較する

最も基本的な使い方は、2つの整数を比較する方法です。

#include <algorithm>
#include <iostream>

int main()
{
    int x = 8;
    int y = 3;

    int result = std::min(x, y);

    std::cout << result << '\n';
}

実行結果は3です。

小数を比較する

doublefloatなどの浮動小数点数も比較できます。

#include <algorithm>
#include <iostream>

int main()
{
    double x = 3.14;
    double y = 2.71;

    double result = std::min(x, y);

    std::cout << result << '\n';
}

実行結果は2.71です。

文字を比較する

char型も比較できます。

#include <algorithm>
#include <iostream>

int main()
{
    char a = 'A';
    char b = 'Z';

    char result = std::min(a, b);

    std::cout << result << '\n';
}

実行結果はAです。

ただし、文字は言語的な順番ではなく、文字コードに基づいて比較されます。

文字列を比較する

std::stringstd::minで比較できます。

#include <algorithm>
#include <iostream>
#include <string>

int main()
{
    std::string a = "apple";
    std::string b = "banana";

    std::string result = std::min(a, b);

    std::cout << result << '\n';
}

実行結果は次のとおりです。

apple

std::stringは、基本的に辞書式の順序で比較されます。

3つ以上の値から最小値を取得する

初期化リストを使用する

C++11以降では、初期化リストを使用して3つ以上の値を比較できます。

#include <algorithm>
#include <iostream>

int main()
{
    int result = std::min({10, 4, 7, 2, 9});

    std::cout << result << '\n';
}

実行結果は2です。

変数を指定することもできます。

int a = 10;
int b = 5;
int c = 8;

int result = std::min({a, b, c});

この場合、resultには5が代入されます。

std::minを入れ子にする

初期化リストを使わず、std::minを入れ子にする方法もあります。

int result = std::min(a, std::min(b, c));

ただし、3つ以上の値を単純に比較する場合は、初期化リストを使用した方が読みやすくなります。

int result = std::min({a, b, c});

空の初期化リストは使用しない

初期化リスト版のstd::minには、1つ以上の値を指定する必要があります。

次のコードは使用できません。

auto result = std::min({});

この場合、要素型を推論できないため、通常はコンパイルエラーになります。

型を明示した場合でも、空の初期化リストは前提条件を満たさないため使用してはいけません。

auto result = std::min<int>({});

std::minの戻り値

2引数版は参照を返す

2引数版のstd::minは、単純な値ではなく、選ばれた引数へのconst参照を返します。

template<class T>
constexpr const T& min(const T& a, const T& b);

例えば、次のコードではresultaまたはbのどちらかを参照します。

#include <algorithm>
#include <iostream>

int main()
{
    int a = 10;
    int b = 20;

    const int& result = std::min(a, b);

    std::cout << result << '\n';
}

autoで受け取ると値になる

次のようにautoで受け取る場合、通常は参照が外れ、値としてコピーされます。

auto result = std::min(a, b);

abint型であれば、resultint型になります。

参照として受け取りたい場合は、次のように記述します。

const auto& result = std::min(a, b);

ただし、参照先であるabが、resultより長く存在している必要があります。

初期化リスト版は値を返す

初期化リスト版のstd::minは、最小要素のコピーを返します。

int result = std::min({a, b, c});

2引数版のように、元の要素への参照を返すわけではありません。

そのため、大きなクラスオブジェクトを比較する場合は、コピーコストが発生する可能性があります。

一時オブジェクトとダングリング参照に注意する

危険な参照の受け取り方

次のコードには注意が必要です。

const int& result = std::min(10, 20);

1020は一時オブジェクトです。

std::minはそのどちらかへの参照を返しますが、文の終了時に一時オブジェクトは破棄されます。

そのため、次の文以降ではresultが無効な参照になります。

const int& result = std::min(10, 20);

// resultは無効な参照

このような状態をダングリング参照と呼びます。

値として受け取れば安全

一時オブジェクトを渡す場合は、値として受け取ります。

int result = std::min(10, 20);

または、次のように記述します。

auto result = std::min(10, 20);

std::stringなどのクラスでも同様です。

危険な例は次のとおりです。

const std::string& result =
    std::min(std::string("apple"), std::string("banana"));

安全な例は次のとおりです。

std::string result =
    std::min(std::string("apple"), std::string("banana"));

参照で受け取る明確な理由がない場合は、値として受け取る方が安全です。

2つの値が同じ場合の動作

2つの値が同等の場合、std::minは第1引数を返します。

int a = 10;
int b = 10;

const int& result = std::min(a, b);

この場合、resultaを参照します。

std::minの動作は、概念的には次のように表せます。

return b < a ? b : a;

第2引数が第1引数より小さい場合だけ第2引数を返し、それ以外の場合は第1引数を返します。

ただし、実際の標準ライブラリ実装が必ずこのコードで書かれているという意味ではありません。

異なる型を指定する場合の注意点

intdoubleはそのまま比較できない

次のコードは、通常コンパイルできません。

int a = 10;
double b = 5.5;

auto result = std::min(a, b);

2引数版のstd::minは、両方の引数から同じテンプレート型Tを推論します。

第1引数からはint、第2引数からはdoubleが推論されるため、型が一致しません。

明示的にキャストする

一方の値をもう一方と同じ型に変換できます。

double result = std::min(static_cast<double>(a), b);

テンプレート引数を明示する

比較する型を明示する方法もあります。

double result = std::min<double>(a, b);

この場合、adoubleへ変換されます。

型変換による情報の損失に注意する

次のコードでは、小数部分が失われます。

double value = 5.8;
int number = 10;

int result = std::min<int>(value, number);

valueintへ変換されるため、5.8ではなく5として比較されます。

型を明示する場合は、より広い範囲を表現できる型を選ぶことが重要です。

共通型を使用する

汎用的な処理では、std::common_type_tを利用できます。

#include <algorithm>
#include <type_traits>

int a = 10;
double b = 5.5;

using CommonType = std::common_type_t<decltype(a), decltype(b)>;

CommonType result = std::min(
    static_cast<CommonType>(a),
    static_cast<CommonType>(b)
);

この場合、共通型は通常doubleになります。

std::size_tと整数リテラルの型不一致

std::size_tintは異なる型

次のコードは、型が一致しないためコンパイルエラーになることがあります。

std::size_t size = 100;

auto result = std::min(size, 50);

sizestd::size_t50intです。

次のように型をそろえます。

auto result = std::min(size, std::size_t{50});

または、テンプレート引数を指定します。

auto result = std::min<std::size_t>(size, 50);

C++23以降では、次のような整数リテラルも利用できます。

auto result = std::min(size, 50uz);

ただし、使用するコンパイラとプロジェクトがC++23に対応している必要があります。

符号付き整数と符号なし整数に注意する

型を無理に統一すると値が変わることがある

次のコードは、intunsigned intの型が一致しないため、通常はコンパイルできません。

int count = -1;
unsigned int limit = 10;

auto result = std::min(count, limit);

次のように符号なし型へ統一することはできますが、危険です。

auto result = std::min<unsigned int>(count, limit);

count-1の場合、符号なし整数へ変換され、非常に大きな正の値として扱われる可能性があります。

負の値を含む可能性がある場合は、事前に確認します。

int count = -1;
unsigned int limit = 10;

if (count < 0) {
    // 負の値を処理する
}
else {
    unsigned int result = std::min(
        static_cast<unsigned int>(count),
        limit
    );
}

単にコンパイルを通すためだけに、符号なし型へキャストするべきではありません。

独自の比較関数を使用する

比較関数の基本形

std::minの第3引数には、独自の比較関数を指定できます。

std::min(a, b, comp);

比較関数は、第1引数を第2引数より小さいと扱う場合にtrueを返します。

bool comp(const T& left, const T& right);

絶対値が小さい方を取得する

#include <algorithm>
#include <cmath>
#include <iostream>

int main()
{
    int a = -10;
    int b = 6;

    int result = std::min(
        a,
        b,
        [](int x, int y) {
            return std::abs(x) < std::abs(y);
        }
    );

    std::cout << result << '\n';
}

絶対値はそれぞれ106なので、結果は6です。

文字列の長さで比較する

#include <algorithm>
#include <iostream>
#include <string>

int main()
{
    std::string a = "apple";
    std::string b = "watermelon";

    const std::string& result = std::min(
        a,
        b,
        [](const std::string& x, const std::string& y) {
            return x.size() < y.size();
        }
    );

    std::cout << result << '\n';
}

文字列の辞書順ではなく、文字数が少ない方を取得します。

実行結果はappleです。

構造体を価格で比較する

#include <algorithm>
#include <iostream>
#include <string>

struct Product
{
    std::string name;
    int price;
};

int main()
{
    Product a{"商品A", 3000};
    Product b{"商品B", 2500};

    const Product& cheaper = std::min(
        a,
        b,
        [](const Product& x, const Product& y) {
            return x.price < y.price;
        }
    );

    std::cout << cheaper.name << ": "
              << cheaper.price << "円\n";
}

実行結果は次のとおりです。

商品B: 2500円

一時オブジェクトを比較する場合

次のように、一時オブジェクトを比較して参照で受け取るのは危険です。

const Product& cheaper = std::min(
    Product{"商品A", 3000},
    Product{"商品B", 2500},
    [](const Product& x, const Product& y) {
        return x.price < y.price;
    }
);

一時オブジェクトは文の終了時に破棄されるため、cheaperは無効な参照になります。

値として受け取ります。

Product cheaper = std::min(
    Product{"商品A", 3000},
    Product{"商品B", 2500},
    [](const Product& x, const Product& y) {
        return x.price < y.price;
    }
);

比較関数では<=を使わない

厳密な比較が必要

比較関数には、通常<またはそれに相当する条件を使用します。

正しい例は次のとおりです。

[](int x, int y) {
    return x < y;
}

次のように<=を使用すると、同じ値同士を比較した場合にもtrueになります。

[](int x, int y) {
    return x <= y;
}

比較関数では、原則として次の条件を満たす必要があります。

comp(x, x) == false

そのため、比較関数には<=ではなく<を使います。

比較順序を逆にする

次のように>を使用すると、通常の意味で大きい方を選べます。

int result = std::min(
    a,
    b,
    [](int x, int y) {
        return x > y;
    }
);

ただし、大きい値を取得することが目的であれば、通常はstd::maxを使った方が意図が明確です。

初期化リストと比較関数を組み合わせる

複数の値を独自の条件で比較することもできます。

#include <algorithm>
#include <cmath>
#include <iostream>

int main()
{
    int result = std::min(
        {-15, 8, -3, 12},
        [](int x, int y) {
            return std::abs(x) < std::abs(y);
        }
    );

    std::cout << result << '\n';
}

絶対値が最も小さいのは-3なので、実行結果は-3です。

配列やコンテナの最小値を取得する

std::min_elementを使用する

std::vectorや配列全体から最小要素を探す場合は、std::min_elementを使用します。

#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> values{10, 4, 7, 2, 9};

    auto it = std::min_element(values.begin(), values.end());

    if (it != values.end()) {
        std::cout << *it << '\n';
    }
}

実行結果は2です。

std::min_elementは、最小値そのものではなく、最小要素を指すイテレータを返します。

空のコンテナを確認する

コンテナが空の場合、std::min_elementは終端イテレータを返します。

そのため、逆参照する前に確認が必要です。

auto it = std::min_element(values.begin(), values.end());

if (it != values.end()) {
    int minimum = *it;
}

std::minとの違い

std::minは、指定された値同士を比較します。

std::min(a, b);

std::min_elementは、指定された範囲から最小要素の位置を探します。

std::min_element(values.begin(), values.end());

コンテナ全体を対象にする場合は、std::min_elementが適しています。

C++20のstd::ranges::min

コンテナを直接指定できる

C++20以降では、std::ranges::minを使用できます。

#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> values{10, 4, 7, 2, 9};

    int result = std::ranges::min(values);

    std::cout << result << '\n';
}

従来のstd::minとは異なり、範囲を直接渡せます。

std::ranges::minは値を返す

範囲版のstd::ranges::minは、最小要素への参照やイテレータではなく、最小要素のコピーを返します。

int result = std::ranges::min(values);

大きなオブジェクトを扱う場合は、コピーコストに注意が必要です。

最小要素への参照が必要な場合は、std::ranges::min_elementを使用します。

auto it = std::ranges::min_element(values);

if (it != values.end()) {
    const int& result = *it;
}

プロジェクションを使用する

std::ranges::minでは、構造体の特定メンバーを比較対象にできます。

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

struct Product
{
    std::string name;
    int price;
};

int main()
{
    std::vector<Product> products{
        {"商品A", 3000},
        {"商品B", 2500},
        {"商品C", 4200}
    };

    Product cheapest = std::ranges::min(
        products,
        {},
        &Product::price
    );

    std::cout << cheapest.name << '\n';
}

&Product::priceがプロジェクションです。

価格を取り出して比較する処理を簡潔に記述できます。

コピーを避ける方法

std::ranges::minではProductがコピーされます。

コピーを避けたい場合は、std::ranges::min_elementを使用します。

auto it = std::ranges::min_element(
    products,
    {},
    &Product::price
);

if (it != products.end()) {
    const Product& cheapest = *it;
}

浮動小数点数とNaNの注意点

NaNを含む場合は引数順で結果が変わる

浮動小数点数には、NaNという特殊な値があります。

#include <algorithm>
#include <iostream>
#include <limits>

int main()
{
    double nan = std::numeric_limits<double>::quiet_NaN();
    double value = 10.0;

    std::cout << std::min(nan, value) << '\n';
    std::cout << std::min(value, nan) << '\n';
}

NaNとの比較では、通常、次の両方がfalseになります。

nan < value
value < nan

std::minは、比較上同等である場合に第1引数を返すため、引数の順番によって結果が変わります。

std::min(nan, value);

この場合は、NaNが返されます。

std::min(value, nan);

この場合は、valueが返されます。

NaNを特別に扱う場合はstd::fmin

NaNを無視して数値側を返したい場合は、std::fminを検討します。

#include <cmath>

double result = std::fmin(a, b);

浮動小数点数を扱う場合は、std::minstd::fminの違いを理解して使い分けることが重要です。

ポインタを比較する場合の注意点

参照先ではなくポインタ値が比較される

次のコードでは、abの値ではなく、ポインタ自体が比較されます。

int a = 10;
int b = 20;

int* p1 = &a;
int* p2 = &b;

int* result = std::min(p1, p2);

同じ配列内のポインタなどでは意味のある順序になりますが、無関係なオブジェクトへのポインタを一般的なアドレス順として扱う場合は注意が必要です。

ポインタ全体の順序が必要な場合

ポインタ値を一定の全順序で比較したい場合は、std::lessを使用できます。

#include <functional>

int* result = std::min(
    p1,
    p2,
    std::less<int*>{}
);

参照先の値を比較する場合

通常は、ポインタが指している値を比較する方が自然です。

int* result = std::min(
    p1,
    p2,
    [](const int* x, const int* y) {
        return *x < *y;
    }
);

この場合、abの値を比較し、小さい値を指すポインタが返されます。

constexprとして使用する

std::minは、条件を満たせばコンパイル時に評価できます。

#include <algorithm>

constexpr int value = std::min(10, 20);

static_assert(value == 10);

関数内でも使用できます。

constexpr int getMinimum(int a, int b)
{
    return std::min(a, b);
}

static_assert(getMinimum(3, 8) == 3);

独自の比較関数も、定数式として評価可能であれば利用できます。

constexpr int result = std::min(
    10,
    20,
    [](int a, int b) constexpr {
        return a < b;
    }
);

Windows環境でのminマクロとの衝突

Windows.hが原因でエラーになることがある

Windows関連のヘッダーでは、minmaxがマクロとして定義されることがあります。

そのため、次のコードが不自然なコンパイルエラーになる場合があります。

#include <Windows.h>
#include <algorithm>

int result = std::min(10, 20);

NOMINMAXを定義する

一般的な対策は、Windows.hをインクルードする前にNOMINMAXを定義することです。

#define NOMINMAX
#include <Windows.h>

#include <algorithm>

関数名を括弧で囲む

次のように記述して、マクロ展開を回避できる場合もあります。

int result = (std::min)(10, 20);

ただし、プロジェクト全体を管理できる場合は、NOMINMAXを定義する方が根本的な対策になります。

std::maxstd::minmaxとの使い分け

最小値だけを取得する

auto minimum = std::min(a, b);

最大値だけを取得する

auto maximum = std::max(a, b);

最小値と最大値を同時に取得する

auto [minimum, maximum] = std::minmax(a, b);

複数の値にも使用できます。

auto [minimum, maximum] =
    std::minmax({10, 4, 7, 2, 9});

最小値と最大値の両方が必要な場合は、std::minstd::maxを別々に呼ぶより、std::minmaxの方が比較回数を抑えられることがあります。

std::minmaxの戻り値に注意する

2引数版のstd::minmaxは、参照の組を返します。

std::pair<const T&, const T&>

初期化リスト版は、値の組を返します。

std::pair<T, T>

一時オブジェクトを2引数版へ渡し、戻り値の参照を保持する場合は、std::minと同様にダングリング参照へ注意が必要です。

std::clampとの使い分け

上限だけを設定する場合

値が最大値を超えないようにする場合は、std::minを使用できます。

int requestedCount = 150;
int maximumCount = 100;

int actualCount = std::min(requestedCount, maximumCount);

actualCountは最大でも100になります。

上限と下限を設定する場合

値を一定範囲に収める場合は、std::clampの方が適しています。

#include <algorithm>

int actualCount = std::clamp(requestedCount, 0, 100);

この場合、値は0以上100以下に制限されます。

実務でよく使われる例

一度に処理する件数を制限する

std::size_t remaining = 73;
std::size_t batchSize = 20;

std::size_t currentSize =
    std::min(remaining, batchSize);

分割処理、バッチ処理、ページネーションなどでよく使用されます。

文字列を最大文字数まで切り出す

#include <algorithm>
#include <string>

std::string text = "Hello, C++!";
std::size_t maximumLength = 5;

std::size_t length =
    std::min(text.size(), maximumLength);

std::string shortened = text.substr(0, length);

shortenedにはHelloが格納されます。

text.size()の戻り値はstd::size_tなので、maximumLengthも同じ型にしておくことが重要です。

コンテナの処理件数を制限する

#include <algorithm>
#include <cstddef>
#include <vector>

void process(
    const std::vector<int>& values,
    std::size_t requested
)
{
    const std::size_t count =
        std::min(values.size(), requested);

    for (std::size_t i = 0; i < count; ++i) {
        // values[i]を処理する
    }
}

指定件数がコンテナの要素数を超えていても、範囲外へアクセスすることを防げます。

std::minでよくあるコンパイルエラー

<algorithm>をインクルードしていない

int result = std::min(10, 20);

std::minを使用する場合は、次のヘッダーをインクルードします。

#include <algorithm>

std::を付けていない

int result = min(10, 20);

標準ライブラリの関数はstd名前空間にあります。

int result = std::min(10, 20);

名前の衝突を避けるため、using namespace std;に頼らず、std::minと明示する方が安全です。

引数の型が異なる

int a = 10;
long b = 20;

auto result = std::min(a, b);

型を統一します。

long result = std::min<long>(a, b);

または、明示的にキャストします。

long result = std::min(
    static_cast<long>(a),
    b
);

初期化リストの型が異なる

auto result = std::min({1, 2.5, 3});

intdoubleが混在しているため、型を決定できません。

すべてdoubleにそろえます。

double result = std::min({1.0, 2.5, 3.0});

または、型を明示します。

double result =
    std::min<double>({1, 2.5, 3});

比較関数に<=を使っている

auto result = std::min(
    a,
    b,
    [](int x, int y) {
        return x <= y;
    }
);

比較関数には、通常<を使用します。

auto result = std::min(
    a,
    b,
    [](int x, int y) {
        return x < y;
    }
);

std::minを使用する際の重要なポイント

std::minを安全に使用するためには、次の点を理解しておくことが重要です。

2引数版は参照を返す

const int& result = std::min(a, b);

参照先の変数が有効な間だけ使用できます。

一時オブジェクトの結果は値で受け取る

int result = std::min(10, 20);

一時オブジェクトを渡した結果を参照で保持してはいけません。

引数の型を統一する

double result = std::min<double>(a, b);

異なる型を比較する場合は、変換後の値が意図どおりになるか確認します。

複数の値には初期化リストを使用する

int result = std::min({a, b, c});

ただし、初期化リスト版は値をコピーして返します。

コンテナにはstd::min_elementを使用する

auto it = std::min_element(
    values.begin(),
    values.end()
);

C++20以降では、std::ranges::minstd::ranges::min_elementも利用できます。

比較関数には厳密な比較条件を指定する

return x < y;

<=ではなく、通常は<を使用します。

まとめ

std::minは、2つ以上の値から最小値を取得できる便利な標準ライブラリ関数です。

基本的な使い方は次のとおりです。

#include <algorithm>

int a = 10;
int b = 20;

int minimum = std::min(a, b);

ただし、2引数版が参照を返すことや、異なる型をそのまま渡せないことには注意が必要です。

特に、一時オブジェクトを渡した結果を参照で保持すると、ダングリング参照が発生します。

const int& result = std::min(10, 20);

安全性を重視する場合は、次のように値として受け取ります。

int result = std::min(10, 20);

また、コンテナ全体から最小要素を探す場合はstd::min_element、C++20以降で範囲を直接扱う場合はstd::ranges::minstd::ranges::min_elementを使い分けることが重要です。

以上、C++のstd::minについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次