C++ replace·replace_if·replace_copy: 값 치환과 transform 중 무엇을 쓸까

이 글의 핵심

치환 알고리즘은 원본을 직접 수정하는지 복사본을 만드는지에 따라 용도가 갈리는데, 이름이 비슷해 헷갈리기 쉽습니다. 여러 값을 한 번에 치환하는 방법, 반복자 무효화 주의점, 부분 문자열 치환은 string::replace로 해야 하는 이유를 짚고 텍스트·데이터 전처리에 적용하는 예제로 마무리합니다.

들어가며

C++ STL의 replace 알고리즘은 컨테이너의 요소를 효율적으로 치환할 수 있게 해줍니다. 값 기반 치환과 조건 기반 치환을 모두 지원합니다.


반복문 대신 replace를 쓰는 이유

for 문으로 돌면서 if (x == 2) x = 9;를 쓰는 것과 std::replace는 성능상 차이가 거의 없습니다. 둘 다 원소를 한 번씩 훑는 O(n)이고, 최적화된 빌드에서는 비슷한 기계어가 나옵니다. 그럼에도 알고리즘 함수를 쓰는 이유는 의도가 이름에 드러나기 때문입니다. std::replace(v.begin(), v.end(), 2, 9) 한 줄은 “2를 9로 바꾼다” 외의 일을 하지 않는다는 것이 보장되지만, 직접 쓴 반복문은 안을 읽어 봐야 인덱스를 잘못 다루지 않았는지, 중간에 다른 일을 하지 않는지 알 수 있습니다. 저는 코드 리뷰에서 “이 루프가 정확히 무엇을 하나”를 추적하는 시간이 이런 이름 있는 알고리즘으로 바꾸면 눈에 띄게 줄어드는 것을 여러 번 경험했습니다.

치환 알고리즘 네 개는 두 가지 축의 조합입니다. 무엇을 바꿀지를 값으로 정하느냐(replace) 조건 함수로 정하느냐(_if), 그리고 결과를 어디에 둘지를 원본으로 하느냐 다른 곳에 복사하느냐(_copy)입니다. 이 축을 알면 이름만 보고 동작을 알 수 있습니다. 네 개 모두 원소의 개수는 바꾸지 않고 값만 바꾼다는 점이 앞 글의 remove와 다릅니다.

std::replace로 특정 값 바꾸기

정수 벡터에서 값 치환

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

int main() {
    std::vector<int> v = {1, 2, 3, 2, 4, 2, 5};
    
    std::cout << "원본: ";
    for (int x : v) {
        std::cout << x << " ";  // 1 2 3 2 4 2 5
    }
    std::cout << std::endl;
    
    // 2를 9로 치환
    std::replace(v.begin(), v.end(), 2, 9);
    
    std::cout << "치환 후: ";
    for (int x : v) {
        std::cout << x << " ";  // 1 9 3 9 4 9 5
    }
    std::cout << std::endl;
}

문자열의 문자 단위 치환

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

int main() {
    std::string text = "Hello World";
    
    // 공백을 밑줄로 치환
    std::replace(text.begin(), text.end(), ' ', '_');
    std::cout << text << std::endl;  // Hello_World
    
    // 특정 문자 치환
    std::string code = "int x = 10;";
    std::replace(code.begin(), code.end(), ' ', '\t');
    std::cout << code << std::endl;  // int	x	=	10;
}

함수 시그니처:

template<class ForwardIt, class T>
void replace(ForwardIt first, ForwardIt last,
             const T& old_value, const T& new_value);

핵심 개념:

  • 원본 수정: 컨테이너를 직접 수정
  • 모든 일치: 일치하는 모든 요소 치환
  • 시간 복잡도: O(n)

시그니처에서 old_value와 new_value가 같은 템플릿 매개변수 T를 쓴다는 점이 컴파일 에러의 흔한 원인입니다. std::vector<double>에서 std::replace(v.begin(), v.end(), 2, 9.5)처럼 정수와 실수를 섞어 넘기면 T가 int인지 double인지 정할 수 없어 “deduced conflicting types for parameter ‘T’ (‘int’ and ‘double’)” 에러가 납니다. 2.0처럼 타입을 맞추면 해결됩니다. 반대로 문자열에서는 'l'과 "L"(문자와 문자열 리터럴)을 섞으면 비슷한 에러가 나는데, std::replace는 원소 하나를 원소 하나로 바꾸는 알고리즘이라 문자 대 문자만 가능합니다(아래 “std::string::replace와 혼동” 참고).

또 하나 알려진 함정은 old_value와 new_value가 참조로 전달된다는 것입니다. std::replace(v.begin(), v.end(), v[0], 9)처럼 범위 안의 원소를 그대로 넘기면, 알고리즘이 v[0]을 9로 바꾸는 순간 비교 기준인 old_value도 9가 되어 이후에는 9를 9로 바꾸는 엉뚱한 동작을 합니다. 기준 값은 먼저 지역 변수에 복사해 두고 넘기세요.


std::replace_if로 조건에 맞는 값 바꾸기

람다 조건으로 치환

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

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 짝수를 0으로 치환
    std::replace_if(v.begin(), v.end(),
         [](int x) { return x % 2 == 0; }, 0);
    
    for (int x : v) {
        std::cout << x << " ";  // 1 0 3 0 5 0 7 0 9 0
    }
    std::cout << std::endl;
}

여러 조건을 묶은 predicate

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

int main() {
    std::vector<int> scores = {45, 67, 89, 34, 92, 78, 56};
    
    // 60점 미만을 0으로
    std::replace_if(scores.begin(), scores.end(),
         [](int score) { return score < 60; }, 0);
    
    std::cout << "점수: ";
    for (int score : scores) {
        std::cout << score << " ";  // 0 67 89 0 92 78 0
    }
    std::cout << std::endl;
}

replace_if의 조건 함수(predicate)는 원소 하나를 받아 bool을 반환하는 호출 가능한 객체면 무엇이든 됩니다. 람다가 가장 흔하고, 기준값이 바뀌는 경우에는 [threshold](int s) { return s < threshold; }처럼 캡처로 넘깁니다. 조건 함수는 원소를 읽기만 해야 합니다. 매개변수를 int&로 받아 안에서 값을 바꾸거나 호출 횟수를 세는 등의 부수 효과를 넣으면, 표준이 조건 함수의 복사나 호출 방식을 자유롭게 허용하므로 결과를 예측하기 어려워집니다. 매개변수는 int나 const T&로 받는 것이 원칙입니다.

이 예제처럼 점수를 0으로 바꾸는 작업은 정보를 잃는 연산이라는 점도 생각해 둘 만합니다. 한 번 0이 된 점수는 원래 45점이었는지 34점이었는지 알 수 없으므로, 원래 값이 나중에 필요할 수 있다면 다음 절의 replace_copy_if로 사본에 적용하는 편이 안전합니다.


replace_copy로 원본을 두고 결과만 만들기

replace_copy

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

int main() {
    std::vector<int> src = {1, 2, 3, 2, 4, 2, 5};
    std::vector<int> dst;
    
    // 복사하며 치환 (원본 유지)
    std::replace_copy(src.begin(), src.end(), 
                      std::back_inserter(dst), 2, 9);
    
    std::cout << "원본: ";
    for (int x : src) {
        std::cout << x << " ";  // 1 2 3 2 4 2 5 (변경 없음)
    }
    std::cout << std::endl;
    
    std::cout << "복사본: ";
    for (int x : dst) {
        std::cout << x << " ";  // 1 9 3 9 4 9 5
    }
    std::cout << std::endl;
}

std::back_inserter(dst)가 이 예제의 핵심입니다. _copy 계열 알고리즘은 결과를 쓸 출력 반복자만 받을 뿐 목적지 컨테이너의 크기를 늘리지 못합니다. 빈 dst에 dst.begin()을 넘기면 존재하지 않는 원소에 쓰게 되어 미정의 동작이고, 디버그 빌드의 MSVC는 “vector iterator not dereferencable” 같은 assertion으로, 릴리스 빌드에서는 조용한 메모리 오염으로 나타납니다. back_inserter는 쓸 때마다 push_back을 호출해 주므로 크기를 몰라도 안전합니다. 크기를 알고 있다면 std::vector<int> dst(src.size());로 미리 만든 뒤 dst.begin()을 넘기는 방식이 재할당이 없어 조금 더 빠릅니다.

반환값도 유용합니다. _copy 알고리즘은 마지막으로 쓴 위치의 다음을 가리키는 출력 반복자를 돌려주므로, 미리 할당한 버퍼에 쓸 때 실제로 어디까지 채워졌는지 알 수 있습니다. 또 원본과 목적지 범위가 겹치면 안 됩니다. 제자리에서 바꾸고 싶다면 _copy가 아닌 replace를 쓰세요.

replace_copy_if

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

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    std::vector<int> dst;
    
    // 짝수를 0으로 치환하며 복사
    std::replace_copy_if(src.begin(), src.end(),
                         std::back_inserter(dst),
                          [](int x) { return x % 2 == 0; }, 0);
    
    std::cout << "원본: ";
    for (int x : src) {
        std::cout << x << " ";  // 1 2 3 4 5 6 7 8 9 10
    }
    std::cout << std::endl;
    
    std::cout << "복사본: ";
    for (int x : dst) {
        std::cout << x << " ";  // 1 0 3 0 5 0 7 0 9 0
    }
    std::cout << std::endl;
}

네 가지 치환 알고리즘 비교

원본 수정 여부와 조건 지원

알고리즘원본 수정조건시간 복잡도
replace✅값 비교O(n)
replace_if✅PredicateO(n)
replace_copy❌값 비교O(n)
replace_copy_if❌PredicateO(n)

함수 시그니처

// 값 치환 (원본 수정)
template<class ForwardIt, class T>
void replace(ForwardIt first, ForwardIt last,
             const T& old_value, const T& new_value);

// 조건 치환 (원본 수정)
template<class ForwardIt, class UnaryPredicate, class T>
void replace_if(ForwardIt first, ForwardIt last,
                UnaryPredicate pred, const T& new_value);

// 복사하며 값 치환
template<class InputIt, class OutputIt, class T>
OutputIt replace_copy(InputIt first, InputIt last, OutputIt d_first,
                      const T& old_value, const T& new_value);

// 복사하며 조건 치환
template<class InputIt, class OutputIt, class UnaryPredicate, class T>
OutputIt replace_copy_if(InputIt first, InputIt last, OutputIt d_first,
                         UnaryPredicate pred, const T& new_value);

치환할 때 자주 틀리는 네 가지

원본을 바꾸는지 복사하는지 헷갈림

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

int main() {
    std::vector<int> v = {1, 2, 3, 2, 4};
    
    // replace: 원본 수정
    std::replace(v.begin(), v.end(), 2, 9);
    std::cout << "원본 수정: ";
    for (int x : v) {
        std::cout << x << " ";  // 1 9 3 9 4
    }
    std::cout << std::endl;
    
    // ✅ 원본 유지하려면 replace_copy
    std::vector<int> v2 = {1, 2, 3, 2, 4};
    std::vector<int> dst;
    std::replace_copy(v2.begin(), v2.end(), 
                      std::back_inserter(dst), 2, 9);
    
    std::cout << "원본: ";
    for (int x : v2) {
        std::cout << x << " ";  // 1 2 3 2 4 (변경 없음)
    }
    std::cout << std::endl;
}

여러 값을 한 번에 바꾸려고 replace를 반복 호출함

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

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    
    // ❌ 비효율적: 여러 번 순회
    std::replace(v.begin(), v.end(), 2, 0);
    std::replace(v.begin(), v.end(), 4, 0);
    
    // ✅ 효율적: 한 번 순회
    std::vector<int> v2 = {1, 2, 3, 4, 5};
    std::replace_if(v2.begin(), v2.end(),
         [](int x) { return x == 2 || x == 4; }, 0);
    
    for (int x : v2) {
        std::cout << x << " ";  // 1 0 3 0 5
    }
    std::cout << std::endl;
}

std::string::replace와 혼동

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

int main() {
    std::string text = "hello world";
    
    // std::replace (알고리즘): 문자 하나씩 치환
    std::replace(text.begin(), text.end(), 'l', 'L');
    std::cout << text << std::endl;  // heLLo worLd
    
    // std::string::replace (멤버 함수): 부분 문자열 치환
    text.replace(0, 5, "HELLO");
    std::cout << text << std::endl;  // HELLO worLd
    
    // 다른 기능!
}

이름이 같아서 가장 자주 혼동하는 부분입니다. std::string::replace(pos, len, str)는 위치와 길이로 지정한 구간을 다른 문자열로 바꾸며, 바꿀 문자열의 길이가 달라도 됩니다. 그래서 “world”를 “C++“로 바꾸는 식의 단어 치환은 이쪽으로 합니다. 다만 한 번에 한 구간만 바꾸므로, 모든 등장을 바꾸려면 find로 위치를 찾아 반복해야 합니다. 이때 흔한 버그가 바꾼 뒤 검색 위치를 새 문자열 길이만큼 옮기지 않는 것입니다. “a”를 “aa”로 바꾸면서 같은 위치부터 다시 찾으면 방금 넣은 “a”를 또 찾아 무한 루프에 빠집니다. pos += replacement.size();로 넘어가야 합니다. 정규식 패턴 치환이 필요하면 regex 글의 std::regex_replace가 편하지만, 성능이 중요한 경로에서는 std::regex가 느리다는 점도 알려져 있습니다.

치환 중 반복자는 무효화되지 않는다

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

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    
    // ✅ replace는 반복자를 무효화하지 않음
    auto it = v.begin();
    std::replace(v.begin(), v.end(), 3, 99);
    
    std::cout << *it << std::endl;  // 1 (여전히 유효)
    
    // 주의: 크기 변경 연산(insert, erase)은 반복자 무효화
}

replace 계열이 반복자를 무효화하지 않는 이유는 원소에 대입만 하고 컨테이너의 구조(크기, 메모리 위치)는 건드리지 않기 때문입니다. 그래서 std::list, std::deque, 일반 배열에도 똑같이 쓸 수 있습니다. 반대로 원소가 const인 컨테이너, 즉 std::set과 std::map의 키에는 쓸 수 없습니다. 정렬 순서를 유지해야 하는 키를 제자리에서 바꾸면 트리 구조가 깨지기 때문에, 반복자가 const 원소를 가리키도록 되어 있어 “assignment of read-only location” 에러가 납니다. map의 값(second)을 바꾸고 싶다면 for (auto& [k, v] : m) 루프를 쓰는 편이 간단합니다.


transform과 replace 중 무엇을 쓸까

동작 차이

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

int main() {
    std::vector<int> v1 = {1, 2, 3, 4, 5};
    std::vector<int> v2 = {1, 2, 3, 4, 5};
    
    // replace: 특정 값만 치환
    std::replace(v1.begin(), v1.end(), 2, 9);
    std::cout << "replace: ";
    for (int x : v1) {
        std::cout << x << " ";  // 1 9 3 4 5
    }
    std::cout << std::endl;
    
    // transform: 모든 요소 변환
    std::transform(v2.begin(), v2.end(), v2.begin(),
         [](int x) { return x * 2; });
    std::cout << "transform: ";
    for (int x : v2) {
        std::cout << x << " ";  // 2 4 6 8 10
    }
    std::cout << std::endl;
}

상황별 선택

상황사용할 알고리즘
특정 값을 다른 값으로replace
조건에 맞는 값만 치환replace_if
모든 요소를 변환transform
원본 유지하며 치환replace_copy

사실 replace_if(first, last, pred, v)는 transform(first, last, first, [&](auto x) { return pred(x) ? v : x; })와 결과가 같습니다. 그래서 둘 중 무엇을 써도 되는 상황이 많은데, 기준은 읽는 사람이 무엇을 알아야 하느냐입니다. “조건에 맞는 것만 한 값으로 바꾼다”면 replace_if가 의도를 더 정확히 전달하고, 조건에 맞지 않는 원소는 건드리지 않는다는 것이 이름으로 보장됩니다. 원소마다 계산이 다르거나 타입이 바뀐다면(vector<int>를 vector<string>으로) transform만 가능합니다. C++20 Ranges에서는 std::ranges::replace(v, 2, 9)처럼 범위를 통째로 넘길 수 있어 begin()/end()를 빠뜨리는 실수도 사라집니다.


센서 값 정제·구두점 치환·최소 점수 보장 예제

오류 값을 평균으로 치환하기

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

int main() {
    // 센서 데이터 (오류 값 -1 포함)
    std::vector<int> sensorData = {23, -1, 25, 24, -1, 26, 23, -1};
    
    // 오류 값을 평균값으로 치환
    int validSum = 0;
    int validCount = 0;
    
    for (int value : sensorData) {
        if (value != -1) {
            validSum += value;
            validCount++;
        }
    }
    
    int average = validSum / validCount;
    
    std::replace(sensorData.begin(), sensorData.end(), -1, average);
    
    std::cout << "정제된 데이터: ";
    for (int value : sensorData) {
        std::cout << value << " ";  // 23 24 25 24 24 26 23 24
    }
    std::cout << std::endl;
}

유효한 값의 합은 121, 개수는 5라서 평균은 24.2인데, int 나눗셈이라 소수점이 버려져 24가 됩니다. 정밀도가 중요하다면 double로 계산해야 합니다. 이 코드에는 경계 조건이 하나 빠져 있습니다. 모든 값이 -1이면 validCount가 0이 되어 validSum / validCount에서 0으로 나누기가 일어나고, 정수 나눗셈이라 대부분의 플랫폼에서 “Floating point exception”(SIGFPE)으로 프로그램이 죽습니다. 센서가 통째로 고장 난 경우처럼 실제로 일어날 수 있는 입력이므로 if (validCount == 0) 처리가 필요합니다.

결측치를 평균으로 채우는 방식 자체의 트레이드오프도 알아 둘 만합니다. 평균 대체는 간단하지만 데이터의 분산을 인위적으로 줄이고, 시계열 데이터라면 앞뒤 값과 동떨어진 값이 끼어들 수 있습니다. 시계열에서는 직전 값으로 채우거나(forward fill) 앞뒤 값의 보간을 쓰는 경우가 많은데, 이런 처리는 “이웃 원소”를 봐야 하므로 원소 하나만 보는 replace로는 표현할 수 없고 인덱스 루프가 필요합니다.

구두점을 공백으로 바꾸기

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

int main() {
    std::string text = "Hello, World! How are you?";
    
    // 구두점을 공백으로
    std::replace_if(text.begin(), text.end(),
         [](char c) { return c == ',' || c == '!' || c == '?'; }, ' ');
    
    std::cout << text << std::endl;
    // Hello  World  How are you 
    
    // 연속된 공백 제거는 unique 사용
}

구두점을 지우지 않고 공백으로 바꾸는 이유는 replace가 문자열 길이를 바꿀 수 없기 때문입니다. 출력에 공백이 두 칸씩 생기는 것도 그래서입니다. 구두점을 아예 없애려면 remove 글의 erase-remove 관용구를, 공백으로 바꾼 뒤 연속 공백을 하나로 줄이려면 std::unique를 이어서 쓰면 됩니다. 구두점 목록을 ||로 나열하는 대신 std::ispunct(static_cast<unsigned char>(c))를 쓰면 모든 ASCII 구두점을 한 번에 처리할 수 있는데, unsigned char로 변환하지 않으면 한글 같은 멀티바이트 문자의 바이트에서 미정의 동작이 되니 주의하세요.

60점 미만을 60점으로 올리기

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

int main() {
    std::vector<int> grades = {95, 45, 78, 34, 89, 67, 23, 91};
    
    // 60점 미만을 60점으로 상향 (최소 점수 보장)
    std::replace_if(grades.begin(), grades.end(),
         [](int grade) { return grade < 60; }, 60);
    
    std::cout << "조정된 점수: ";
    for (int grade : grades) {
        std::cout << grade << " ";  // 95 60 78 60 89 67 60 91
    }
    std::cout << std::endl;
}

“최소값 보장”은 사실 치환보다 범위 제한에 가까운 연산이라, C++17의 std::clamp와 std::transform을 조합해 std::transform(g.begin(), g.end(), g.begin(), [](int x) { return std::clamp(x, 60, 100); });처럼 쓰면 하한과 상한을 한 번에 처리할 수 있습니다. replace_if는 조건에 맞는 원소를 같은 하나의 값으로 바꿀 때 가장 잘 맞고, 원소마다 다른 값이 필요하면 transform이 맞습니다. 이 구분은 아래 “transform과 replace 중 무엇을 쓸까”에서 다시 정리합니다.


여러 단계로 데이터 전처리하기

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <numeric>

class DataPreprocessor {
public:
    // 이상치 제거 (평균으로 치환)
    static void replaceOutliers(std::vector<double>& data, double threshold) {
        double mean = std::accumulate(data.begin(), data.end(), 0.0) / data.size();
        
        std::replace_if(data.begin(), data.end(),
            [mean, threshold](double x) {
                return std::abs(x - mean) > threshold;
            }, mean);
    }
    
    // 음수를 0으로
    static void replaceNegatives(std::vector<int>& data) {
        std::replace_if(data.begin(), data.end(),
             [](int x) { return x < 0; }, 0);
    }
    
    // 결측치 처리
    static void replaceMissing(std::vector<int>& data, int missingValue, int replacement) {
        std::replace(data.begin(), data.end(), missingValue, replacement);
    }
};

int main() {
    // 센서 데이터
    std::vector<double> temperatures = {23.5, 24.0, 100.0, 23.8, 24.2, -50.0, 24.5};
    
    std::cout << "원본: ";
    for (double t : temperatures) {
        std::cout << t << " ";
    }
    std::cout << std::endl;
    
    // 이상치 제거 (평균에서 50 이상 차이)
    DataPreprocessor::replaceOutliers(temperatures, 50.0);
    
    std::cout << "전처리 후: ";
    for (double t : temperatures) {
        std::cout << t << " ";
    }
    std::cout << std::endl;
}

이 예제를 실행하면 100.0과 -50.0이 모두 약 24.29로 바뀝니다. 그런데 결과가 그럴듯해 보이는 것은 운이 좋아서입니다. 평균을 구할 때 이상치 두 개가 포함되는데, 마침 +100과 -50이 서로 상쇄되어 평균이 정상 범위에 가깝게 나왔을 뿐입니다. 이상치가 100 하나뿐이었다면 평균이 약 35까지 끌려 올라가고, 그 값으로 이상치를 채우게 됩니다. 이상치를 찾는 기준 자체가 이상치에 오염되는 문제로, 실무에서는 이상치에 둔감한 중앙값(std::nth_element로 O(n)에 구할 수 있습니다)과 중앙값 절대 편차를 기준으로 쓰는 경우가 많습니다.

코드에도 두 가지 주의점이 있습니다. std::abs를 double에 쓰려면 <cmath>가 필요하며, 빠뜨리면 일부 환경에서 정수 버전 abs(int)가 선택되어 값이 잘리는 조용한 버그가 됩니다. 또 data가 비어 있으면 data.size()가 0이라 평균이 NaN(0.0/0)이 되고, NaN과의 비교는 항상 거짓이라 아무것도 바뀌지 않은 채 조용히 넘어갑니다. 빈 입력은 함수 앞에서 걸러 두는 편이 좋습니다.


치환 알고리즘 요약

  1. replace: 특정 값을 다른 값으로 치환 (원본 수정)
  2. replace_if: 조건에 맞는 값 치환
  3. replace_copy: 복사하며 치환 (원본 유지)
  4. replace_copy_if: 복사하며 조건 치환
  5. 시간 복잡도: 모두 O(n)

이어서 볼 STL 알고리즘


같이 보면 좋은 글


자주 묻는 질문 (FAQ)

Q. std::replace와 std::string::replace는 무엇이 다른가요?

A. 의 std::replace는 범위 안에서 특정 값과 같은 원소를 하나씩 다른 값으로 바꾸는 알고리즘이라, 문자열에서는 ‘l’을 ‘L’로 바꾸는 식의 문자 단위 치환만 할 수 있습니다. std::string::replace는 위치와 길이로 지정한 부분 문자열을 다른 문자열로 바꾸는 멤버 함수입니다. 문자열 안의 단어를 다른 단어로 모두 바꾸려면 find와 string::replace를 반복하거나 의 regex_replace를 써야 합니다.