본문으로 건너뛰기 C++ Replace Algorithms: replace, replace_if & replace_copy

C++ Replace Algorithms: replace, replace_if & replace_copy

C++ Replace Algorithms: replace, replace_if & replace_copy

이 글의 핵심

Replace algorithms substitute old values with new ones, or values matching a predicate, either in place or via copy variants.

Introduction

Replace algorithms substitute old values with new ones, or values matching a predicate, either in place or via copy variants.

#include <algorithm>
#include <vector>
#include <iostream>
std::vector<int> data = {1, 2, 3, 2, 5};
std::replace(data.begin(), data.end(), 2, 99);
// Result: {1, 99, 3, 99, 5}

std::replace

Replaces all occurrences of old_value with new_value in place.

The following example demonstrates the concept in cpp:

#include <algorithm>
#include <vector>
std::vector<int> numbers = {1, 2, 3, 2, 4, 2};
std::replace(numbers.begin(), numbers.end(), 2, 99);
// Result: {1, 99, 3, 99, 4, 99}

Signature:

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

std::replace_if

Replaces all elements satisfying a predicate.

The following example demonstrates the concept in cpp:

#include <algorithm>
#include <vector>
std::vector<int> numbers = {-1, 2, -3, 4, -5};
std::replace_if(numbers.begin(), numbers.end(),
                [](int x) { return x < 0; },
                0);
// Result: {0, 2, 0, 4, 0}

Signature:

template<class ForwardIt, class UnaryPredicate, class T>
void replace_if(ForwardIt first, ForwardIt last,
                UnaryPredicate pred, const T& new_value);

std::replace_copy

Copies elements to a new range, replacing specified values.

#include <algorithm>
#include <vector>
std::vector<int> source = {1, 2, 3, 2, 5};
std::vector<int> dest;
std::replace_copy(source.begin(), source.end(),
                  std::back_inserter(dest),
                  2, 99);
// source: {1, 2, 3, 2, 5} (unchanged)
// dest: {1, 99, 3, 99, 5}

std::replace_copy_if

Copies elements, replacing those matching a predicate.

#include <algorithm>
#include <vector>
std::vector<int> source = {1, -2, 3, -4, 5};
std::vector<int> dest;
std::replace_copy_if(source.begin(), source.end(),
                     std::back_inserter(dest),
                     [](int x) { return x < 0; },
                     0);
// dest: {1, 0, 3, 0, 5}

Real-world applications

1. Data cleaning: sensor error correction

#include <algorithm>
#include <vector>
struct SensorReading {
    double value;
    bool isValid() const { return value >= 0 && value <= 100; }
};
void cleanSensorData(std::vector<SensorReading>& readings) {
    std::replace_if(readings.begin(), readings.end(),
                    [](const SensorReading& r) { return !r.isValid(); },
                    SensorReading{0.0});
}
// Usage
std::vector<SensorReading> data = {{25.5}, {-999}, {30.2}, {150}};
cleanSensorData(data);
// Result: {{25.5}, {0.0}, {30.2}, {0.0}}

2. Text processing: normalize whitespace

#include <algorithm>
#include <string>
#include <cctype>
std::string normalizeWhitespace(const std::string& text) {
    std::string result = text;
    
    // Replace tabs and newlines with spaces
    std::replace(result.begin(), result.end(), '\t', ' ');
    std::replace(result.begin(), result.end(), '\n', ' ');
    
    // Replace multiple spaces with single space
    auto newEnd = std::unique(result.begin(), result.end(),
                              [](char a, char b) {
                                  return a == ' ' && b == ' ';
                              });
    result.erase(newEnd, result.end());
    
    return result;
}
// Test
std::string text = "Hello\t\tWorld\n\nTest";
auto normalized = normalizeWhitespace(text);
// Result: "Hello World Test"

3. Grade adjustment: apply floor

#include <algorithm>
#include <vector>
void applyGradeFloor(std::vector<int>& grades, int minGrade = 50) {
    std::replace_if(grades.begin(), grades.end(),
                    [minGrade](int grade) { return grade < minGrade; },
                    minGrade);
}
// Usage
std::vector<int> grades = {85, 42, 90, 35, 78};
applyGradeFloor(grades, 50);
// Result: {85, 50, 90, 50, 78}

4. Configuration sanitization

#include <algorithm>
#include <map>
#include <string>
void sanitizeConfig(std::map<std::string, std::string>& config) {
    for (auto& [key, value] : config) {
        // Replace sensitive placeholders
        if (value == "${SECRET}") {
            value = "***REDACTED***";
        }
        
        // Replace invalid paths
        std::replace(value.begin(), value.end(), '\\', '/');
    }
}

replace vs std::string::replace

<algorithm> std::replace: Element-wise

#include <algorithm>
#include <string>
std::string text = "hello world";
std::replace(text.begin(), text.end(), 'l', 'L');
// Result: "heLLo worLd"

std::string::replace: Substring

#include <string>
std::string text = "hello world";
text.replace(0, 5, "Hi");  // Replace "hello" with "Hi"
// Result: "Hi world"

Key difference: <algorithm> works on individual elements, string::replace works on substrings.

replace vs transform

std::replace: Specific value substitution

std::vector<int> data = {1, 2, 3, 2, 5};
std::replace(data.begin(), data.end(), 2, 99);
// Only 2s become 99

std::transform: Apply function to all elements

std::vector<int> data = {1, 2, 3, 4, 5};
std::transform(data.begin(), data.end(), data.begin(),
               [](int x) { return x * 2; });
// All elements doubled: {2, 4, 6, 8, 10}

Performance benchmarks

Test setup: GCC 13, -O3, 1M element std::vector<int>

OperationTime (μs)Notes
std::replace285In-place, single pass
std::replace_if320Predicate overhead
std::replace_copy450Allocation + copy
Manual loop290Similar to replace
Key insight: replace is highly optimized. Predicate versions have small overhead for function calls.

Common pitfalls and fixes

Pitfall 1: Multiple sequential replaces

// ❌ Inefficient: 3 passes
std::replace(data.begin(), data.end(), -1, 0);
std::replace(data.begin(), data.end(), -2, 0);
std::replace(data.begin(), data.end(), -3, 0);
// ✅ Better: single pass with predicate
std::replace_if(data.begin(), data.end(),
                [](int x) { return x < 0; },
                0);

Pitfall 2: Forgetting output space for replace_copy

std::vector<int> source = {1, 2, 3};
std::vector<int> dest;
// ❌ Undefined behavior: dest is empty
std::replace_copy(source.begin(), source.end(),
                  dest.begin(), 2, 99);
// ✅ Fix 1: Pre-allocate
std::vector<int> dest(source.size());
std::replace_copy(source.begin(), source.end(),
                  dest.begin(), 2, 99);
// ✅ Fix 2: Use back_inserter
std::vector<int> dest;
std::replace_copy(source.begin(), source.end(),
                  std::back_inserter(dest), 2, 99);

Pitfall 3: Confusing with std::string::replace

std::string text = "hello";
// ❌ Wrong: trying to use string::replace like algorithm
// text.replace('l', 'L');  // Error: no such overload
// ✅ Correct: use algorithm
std::replace(text.begin(), text.end(), 'l', 'L');
// Or use string::replace for substrings
text.replace(text.find("ll"), 2, "LL");

Pitfall 4: Modifying during iteration

// ❌ Dangerous: modifying while iterating
for (auto it = data.begin(); it != data.end(); ++it) {
    if (*it == 2) {
        *it = 99;  // OK, but use replace instead
    }
}
// ✅ Better: use algorithm
std::replace(data.begin(), data.end(), 2, 99);

Advanced: Custom comparators

Case-insensitive string replacement

#include <algorithm>
#include <string>
#include <cctype>
bool caseInsensitiveEqual(char a, char b) {
    return std::tolower(a) == std::tolower(b);
}
void replaceIgnoreCase(std::string& str, char oldChar, char newChar) {
    std::replace_if(str.begin(), str.end(),
                    [oldChar](char c) {
                        return std::tolower(c) == std::tolower(oldChar);
                    },
                    newChar);
}
// Usage
std::string text = "Hello World";
replaceIgnoreCase(text, 'l', 'X');
// Result: "HeXXo WorXd"

Replace with tolerance (floating-point)

#include <algorithm>
#include <vector>
#include <cmath>
void replaceApprox(std::vector<double>& data, 
                   double target, 
                   double replacement,
                   double tolerance = 1e-6) {
    std::replace_if(data.begin(), data.end(),
                    [target, tolerance](double x) {
                        return std::abs(x - target) < tolerance;
                    },
                    replacement);
}
// Usage
std::vector<double> values = {1.0, 2.000001, 3.0, 1.999999};
replaceApprox(values, 2.0, 99.0, 0.001);
// Result: {1.0, 99.0, 3.0, 99.0}

Parallel execution (C++17)

#include <algorithm>
#include <execution>
#include <vector>
std::vector<int> data(10'000'000, 2);
// Sequential
std::replace(data.begin(), data.end(), 2, 99);
// Parallel (C++17)
std::replace(std::execution::par,
             data.begin(), data.end(), 2, 99);

Benchmark (10M elements):

  • Sequential: 28ms
  • Parallel: 8ms (on 8-core CPU)

Summary

AlgorithmMutatesPredicateOutput
replaceYesNoIn-place
replace_ifYesYesIn-place
replace_copyNoNoNew range
replace_copy_ifNoYesNew range
Time complexity: O(n) for all variants
Space complexity:
  • replace/replace_if: O(1)
  • replace_copy/replace_copy_if: O(n) for output

Next steps


Keywords

std::replace, replace_if, replace_copy, STL, C++, algorithm, data cleaning, text processing


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Replace values in place or into a new range with std::replace, replace_if, replace_copy, and replace_copy_if — O(n) scan…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers C++, Algorithm, replace, transform, STL.