Split string into "n" lengths with padding
My first impression is that we would probably wish to avoid copying and shuffle data as much as possible.

The code as presented does the following:


conditionally remove items (worst case time complexity occurs when every char is a space, in which case it's \$\mathcal{O}(\frac{n^2}{2})\$ (more or less))
append padding (worst case will involve a reallocation plus copy \$\mathcal{O}(n)\$)


I think we can do better, performing the entire operation in linear time and guaranteeing no memory reallocations. This involves iteratively conditionally copying the bytes. We can do this because std::string's iterators are random-access, so we can compute distances in constant time.

I have presented it here as a simple composition of methods in a lightweight engine object. Another way would be to create a state machine which could be chained to another encryption stage:

#include &lt;iostream&gt;
#include &lt;algorithm&gt;

struct squish_engine
{
    squish_engine(std::size_t blockSize, char paddingChar)
            : blockSize(blockSize)
            , paddingChar(paddingChar)
    {

    }

    // useful for initial allocation of destination string
    std::size_t estimate_size(std::size_t plaintext_length) const {
        return plaintext_length + blockSize;
    }

    // a predicate    
    static bool matchesSpace(char c) {
        return c == ' ';
    }

    // operation in terms of iterators    
    template&lt;class FromIter, class ToIter&gt;
    ToIter operator()(FromIter first, FromIter last, ToIter first2) const
    {
        // use stl algorithms where possible
        auto last2 = std::remove_copy_if(first, last, first2, matchesSpace);
        auto pads = std::distance(first2, last2) % blockSize;
        if (pads) {
            pads = blockSize - pads;
        }
        last2 += pads;
        return last2;
    };

    // this is the public interface    
    template&lt;class Iter&gt;
    std::string operator()(Iter first, Iter last) const
    {
        // build an overly-large string to accept the result.
        // we may as well pad it now, as it's no extra cost compared
        // to zero-filling
        std::string result(estimate_size(std::distance(first, last)), paddingChar);
        // our internal operator will compute the end point of the
        // padded area for us
        auto last2 = (*this)(first, last, std::begin(result));

        // so we can simply truncate any padding chars we don't need
        result.erase(last2, result.end());

        // and return the cypher
        return result;
    }

    // convenience interface
    std::string operator()(const std::string&amp; s)
    {
        return (*this)(std::begin(s), std::end(s));
    }

    std::size_t blockSize;
    char paddingChar;
};

// the concept of making a copy while filtering
template&lt;class Filter&gt;
std::string filter_copy(const std::string&amp; source, Filter&amp;&amp; filter)
{
    return filter(source);
}

int main(){

    std::size_t blockSize = 11;
    char paddingChar = 'X';

    std::string str = "Tis' but a scratch!";

    // now we can conceivably choose the encryption engine at the
    // client call site
    auto compressed = filter_copy(str, squish_engine(blockSize, paddingChar));

    std::cout &lt;&lt; compressed &lt;&lt; '\n';
    return 0;
}