메뉴 닫기

STL std::generate 함수 완벽 가이드 – 시퀀스 생성부터 난수 채우기까지


STL std::generate 함수 완벽 가이드 – 시퀀스 생성부터 난수 채우기까지

🧪 반복자 범위에 자동으로 값 채우기, std::generate로 한 번에 끝냅시다

C++을 조금만 다뤄보신 분들이라면 컨테이너에 값을 일일이 넣는 수고스러움을 경험하셨을 겁니다.
특히 난수나 날짜, 일련번호처럼 일정한 규칙이 있거나 반복되는 데이터를 만들어야 할 때 반복문을 매번 작성하기엔 너무 번거롭죠.
그럴 때 정말 유용하게 사용할 수 있는 함수가 바로 std::generate입니다.

<algorithm> 헤더에 포함된 std::generate는 함수 또는 람다를 기반으로 값을 만들어 컨테이너에 자동으로 채워줍니다.
간단한 코드로 깔끔하게 반복 데이터를 구성할 수 있어 생산성과 가독성 모두에서 뛰어난 장점을 제공합니다.
오늘 이 글에서는 std::generate의 기본 사용법부터 실전 예제까지 한 번에 정리해드릴게요.







🔗 std::generate 함수란?

std::generate는 C++ STL의 <algorithm> 헤더에 포함된 함수로, 반복자 범위에 대해 함수를 호출한 결과를 채워넣는 기능을 제공합니다.
말 그대로 어떤 ‘값을 생성(generate)’하는 함수인데, 반복문 없이도 매우 깔끔하게 데이터를 자동 생성할 수 있어 자주 사용됩니다.

이 함수는 내부적으로 지정한 범위의 각 위치에 대해, 사용자가 전달한 함수 또는 람다식을 매번 호출하여 그 결과를 해당 위치에 할당합니다.
즉, 함수가 반환하는 값이 컨테이너 안에 차례대로 들어가는 방식이죠.
난수, 시퀀스, 날짜 등 반복적이지만 규칙이 있는 데이터를 만들 때 매우 효과적입니다.

💎 핵심 포인트:
std::generate는 컨테이너의 값을 초기화하거나 갱신할 때, 반복문 없이도 간결하고 직관적인 코드 작성을 도와줍니다.

예를 들어 0부터 시작하는 일련번호를 벡터에 채우고 싶다면, 간단한 람다를 전달하는 것만으로 해결할 수 있습니다.
std::generate(vec.begin(), vec.end(), [&i]() { return i++; });처럼 말이죠.

CODE BLOCK
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v(10);
    int n = 1;
    std::generate(v.begin(), v.end(), [&n]() { return n++; });

    for (int x : v)
        std::cout << x << " ";
}
// 출력: 1 2 3 4 5 6 7 8 9 10

이처럼 std::generate는 반복자 구간과 함수만 있으면 손쉽게 데이터를 자동 생성할 수 있어, 반복문보다 훨씬 간단하고 오류도 줄일 수 있습니다.
다음 단계에서는 이 함수의 기본 문법과 작동 방식에 대해 더 자세히 알아볼게요.


🛠️ 기본 문법과 반복자 범위

std::generate의 기본 구조는 매우 단순합니다.
반복자 범위와 값을 생성할 함수 또는 람다를 전달하면 끝이죠.
이 함수는 지정된 범위의 각 요소에 대해 생성 함수를 한 번씩 호출하여, 그 결과를 해당 위치에 저장합니다.

💎 핵심 포인트:
std::generate(시작 반복자, 끝 반복자, 생성 함수) 형식으로 사용하며, 모든 위치에 생성 함수의 결과값이 각각 채워집니다.

CODE BLOCK
std::vector<int> v(5);
std::generate(v.begin(), v.end(), []() { return 42; });
// v = {42, 42, 42, 42, 42}

위 예제는 반복자 범위 전체에 42라는 값을 채워넣는 간단한 예시입니다.
함수는 매 반복마다 호출되므로, 생성되는 값이 고정되어 있든, 내부 상태에 따라 바뀌든 자유롭게 설계할 수 있습니다.

  • 🔁[first, last) 범위는 첫 반복자는 포함하고, 마지막 반복자는 포함하지 않습니다
  • ⚠️컨테이너가 비어 있거나 범위가 잘못되면 아무 작업도 수행되지 않습니다
  • 📌람다 함수, 함수 포인터, 함수 객체 모두 사용 가능합니다

이처럼 반복자 범위만 잘 지정하면, 굳이 반복문을 작성하지 않고도 컨테이너를 초기화하거나 변경할 수 있습니다.
다음 단계에서는 특히 자주 쓰이는 람다 함수와의 조합에 대해 알아보겠습니다.







⚙️ 람다 함수와 함께 활용하는 방법

C++11 이후 도입된 람다 함수는 std::generate와 함께 사용할 때 가장 강력한 조합이 됩니다.
람다를 사용하면 반복적으로 호출되는 생성 로직을 함수 외부에 선언하지 않고, inline으로 작성할 수 있어 코드가 훨씬 간결해지죠.

람다 함수는 [] 캡처 리스트를 통해 외부 변수를 참조할 수 있으므로, 시퀀스 번호처럼 상태가 변하는 값을 손쉽게 처리할 수 있습니다.
예를 들어 100부터 시작하는 정수 시퀀스를 채워넣는 경우 다음과 같이 작성할 수 있습니다.

CODE BLOCK
std::vector<int> v(5);
int seed = 100;
std::generate(v.begin(), v.end(), [&seed]() { return seed++; });
// 결과: 100, 101, 102, 103, 104

람다의 캡처 리스트는 [&] 또는 [&변수명] 형식으로 외부 변수의 참조를 전달하며, 내부에서 값을 자유롭게 변경할 수 있습니다.
이 덕분에 단순한 패턴부터 복잡한 수식 계산까지 모두 람다 하나로 표현이 가능하죠.

💎 핵심 포인트:
람다 함수를 사용하면 상태를 가진 생성 로직도 inline으로 표현 가능해지고, 반복문보다 훨씬 직관적이고 깔끔한 코드 작성을 도와줍니다.

실제로 실무에서는 난수 생성이나 UUID 생성처럼 외부 라이브러리와의 연동에서도 람다 기반 generate 패턴이 자주 쓰입니다.
다음 글에서는 이런 실제 활용 예제들을 좀 더 구체적으로 소개해드릴게요.


🎲 난수와 시퀀스 생성 예제

std::generate는 난수나 시퀀스 번호처럼 자동 증가하거나 무작위로 생성되는 값을 컨테이너에 채워 넣을 때 특히 유용합니다.
복잡한 for문을 작성하지 않아도, 단 한 줄로 원하는 값을 자동 채워줄 수 있죠.

CODE BLOCK
// 1부터 시작하는 시퀀스 번호
std::vector<int> ids(10);
int i = 1;
std::generate(ids.begin(), ids.end(), [&i]() { return i++; });
// 결과: 1, 2, 3, ..., 10

// 0~99 범위의 난수 채우기
std::vector<int> randoms(5);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(0, 99);
std::generate(randoms.begin(), randoms.end(), [&]() { return dist(gen); });

첫 번째 예제는 단순한 일련번호를 벡터에 채우는 코드이고, 두 번째는 <random> 헤더를 활용해 난수를 벡터에 채워 넣는 예시입니다.
둘 다 std::generate와 람다만으로 구현할 수 있어 매우 직관적입니다.

  • 🎯시퀀스 번호는 외부 변수 증가 방식으로 구현
  • 🎲난수는 std::random 계열 함수와 조합
  • 🔒캡처 변수는 반드시 범위 내에서 안전하게 관리

실제 프로젝트에서는 이런 방식으로 사용자 ID 자동 생성, 가상의 테스트 데이터 생성, 또는 게임 로직에서의 무작위 이벤트 설정에도 std::generate가 자주 사용됩니다.
이제 generate의 파생 함수인 generate_n과의 차이점도 알아볼까요?







💡 std::generate와 generate_n 차이

C++ STL에서는 std::generatestd::generate_n 두 가지 유사한 함수가 존재합니다.
이 둘은 모두 값을 생성해 컨테이너에 채워넣는다는 점에서는 같지만, 동작 방식과 사용 목적에 중요한 차이가 있습니다.

generate는 반복자 범위를 지정하는 방식인 반면, generate_n은 시작 위치 + 개수를 지정합니다.
즉, 컨테이너가 비어 있거나 부분적으로만 값을 채워야 할 때 generate_n이 더 적합한 경우가 많습니다.

CODE BLOCK
// std::generate
std::vector<int> v1(5);
int a = 0;
std::generate(v1.begin(), v1.end(), [&]() { return ++a; });

// std::generate_n
std::vector<int> v2(10, 0);
int b = 100;
std::generate_n(v2.begin() + 3, 4, [&]() { return b += 10; });
// v2: 앞의 3개는 0, 이후 4개는 110, 120, 130, 140

  • 📌generate: [first, last) 반복자 범위 전체 적용
  • 🔢generate_n: 시작 위치 + n개만 채움 (last 반복자 필요 없음)
  • 🛠️부분 초기화나 조건부 채우기에 generate_n 활용

generate와 generate_n은 목적에 따라 선택적으로 사용하면 됩니다.
전체 컨테이너를 채울 경우엔 generate, 특정 위치에만 일부 값을 채워야 한다면 generate_n이 더 적절하죠.
두 함수 모두 람다 또는 함수 포인터를 자유롭게 사용할 수 있으므로 상황에 맞게 유연하게 적용해보세요.


자주 묻는 질문 (FAQ)

std::generate는 어떤 경우에 가장 많이 사용되나요?
난수 생성, 일련번호 부여, 테스트용 더미 데이터 생성 등 반복적으로 값을 초기화할 때 자주 사용됩니다.
generate와 generate_n의 가장 큰 차이는 뭔가요?
generate는 반복자 범위를 사용하고, generate_n은 시작 위치와 채울 개수를 지정합니다. generate_n은 컨테이너의 일부분만 채울 때 유리합니다.
람다 대신 함수 포인터도 사용할 수 있나요?
네, 람다뿐 아니라 함수 포인터나 함수 객체(functor)도 std::generate에 사용할 수 있습니다. 반환값이 있어야 하며 인자는 없어야 합니다.
generate로 생성되는 값은 중복되지 않게 할 수 있나요?
생성 함수 내부에서 상태를 관리하거나, 셋(Set)을 함께 활용하면 중복을 방지할 수 있습니다. 다만 자체적으로 중복 체크 기능은 없습니다.
사용할 반복자에 제약이 있나요?
generate는 입력 반복자 이상의 반복자이면 사용 가능하며, 대부분의 컨테이너(begin~end)에서 정상 작동합니다.
generate 사용 시 주의할 점은 무엇인가요?
생성 함수는 side effect가 없어야 하며, 외부 변수를 캡처할 경우 동기화나 범위 문제에 유의해야 합니다.
generate로 복잡한 구조체도 채울 수 있나요?
가능합니다. 람다 함수에서 구조체를 생성해 반환하면 벡터 같은 컨테이너에 해당 구조체들을 채워 넣을 수 있습니다.
generate_n을 사용하면 컨테이너 크기를 자동으로 확장해주나요?
아닙니다. generate_n은 크기를 확장하지 않으며, 미리 충분한 크기의 컨테이너를 확보한 뒤 사용해야 합니다.



🧪 반복 데이터 자동 생성, std::generate로 끝내보세요

std::generate는 반복적인 데이터를 초기화하거나 랜덤 값, 일련번호처럼 규칙적인 데이터를 생성할 때 매우 유용한 함수입니다.
복잡한 반복문 없이도 간단한 한 줄 코드로 원하는 값들을 채워넣을 수 있어, 코드의 가독성과 유지보수성이 크게 향상되죠.

이 글에서는 std::generate의 기본 개념부터 람다 함수와의 조합, 난수 및 시퀀스 생성 예제, 그리고 generate_n과의 차이까지 꼼꼼히 살펴봤습니다.
실무 코드나 알고리즘 테스트, 게임 개발, 테스트 데이터 구성 등 다양한 상황에서 적극적으로 활용해보세요.

코드 생산성과 품질을 동시에 높이고 싶다면, 이제 반복문 대신 std::generate를 한 번 활용해보세요.
이 작은 함수 하나가 여러분의 코딩 스타일을 훨씬 더 세련되게 바꿔줄 거예요.


🏷️ 관련 태그 : stdgenerate, generate_n, C++자동초기화, 반복자범위, 람다사용법, 난수생성, 일련번호, STL알고리즘, C++유틸함수, 벡터초기화