Implement C++11 multipart sending without copying to a single buffer
Use namespace if you want a namespace

You use your struct only as a way to have merge and split not in the global namespace (and inlined). That's not necessary. We can just use a namespace:

namespace MultipartSplitMerge {
    ...
}


Don't use define for compile-time constants

You define PADDING, but you never undefine it. It's a common word in code, so it's possible that you accidentally break something. Furthermore, it's not in the same namespace as merge and split, as the preprocessor does not care about namespaces.

Instead, use a static constexpr size_t:

static constexpr size_t PADDING = 8;


This has the nice side-effect that PADDING has now a type. The compiler can now warn you when you try to compare it to other, signed types.

Prefer references instead of pointers

Not only are they easier to use, they also are harder to get wrong.

Use const on call-by-reference/-pointer arguments that are not changed

You never change s1, s2 and s3, yet you didn't use const. We could have changed their values accidentally. A const will prevent this:

inline std::string merge(const std::string&amp; s1, const std::string&amp; s2, const std::string&amp; s3) {
    const std::string size_1 = std::to_string(s1.size()); 
    const std::string size_2 = std::to_string(s2.size()); 
    const std::string padded_size_1 = std::string(PADDING - size_1.length(), '0') + size_1;
    const std::string padded_size_2 = std::string(PADDING - size_2.length(), '0') + size_2;
    const std::string ret_str(padded_size_1 + padded_size_2 + s1 + s2 + s3);
    return ret_str; // Add the size of each piece
}

inline void split(const std::string&amp; s, std::string &amp;o1, std::string &amp;o2, std::string &amp;o3) {
    const unsigned int size_1 = std::stoi(s.substr(0, PADDING));
    const unsigned int size_2 = std::stoi(s.substr(PADDING, PADDING));
    o1 = s.substr(2 * PADDING, size_1); 
    o2 = s.substr(2 * PADDING + size_1, size_2); 
    o3 = s.substr(2 * PADDING + size_1 + size_2);
}


Note that all this inline annotations are only necessary if your functions will be in a header.

Don't repeat yourself

You pad your string, but it's a) hard to get and b) repetitive. Write a function to get rid of the duplicate logic:

inline std::string pad(size_t size, char padding, const std::string&amp; original) {
    assert(size &gt;= original.size());
    return std::string(size - original.size(), padding) + original;
}

inline std::string pad_size(const std::string&amp; original) {
    return pad(PADDING, '0', std::to_string(original.size()));
}


If you find an optimization for pad or pad_size, merge gets optimized for free. And it gets a lot shorter:

inline std::string merge(const std::string&amp; s1, const std::string&amp; s2, const std::string&amp; s3) {        
    return pad_size(s1) + pad_size(s2) + s1 + s2 + s3;
}


All code at once

Now let us inspect the reviewed and somewhat rewritten code:

namespace MultipartSplitMerge {
static constexpr size_t FIXED_FIELD_LENGTH = 8;

// Optional `detail' namespace; shows a user that `pad' and `pad_size'
// are implementation details. Only important in a header-only
// library
namespace detail {
inline std::string pad(size_t size, char padding, const std::string &amp;original) {
    assert(size &gt;= original.size());
    return std::string(size - original.size(), padding) + original;
}

inline std::string pad_size(const std::string &amp; original) {
    return pad(FIXED_FIELD_LENGTH, '0', std::to_string(original.size()));
}
} // namespace detail

inline std::string merge(const std::string&amp; s1, const std::string&amp; s2, const std::string&amp; s3) {        
    return detail::pad_size(s1) + detail::pad_size(s2) + s1 + s2 + s3;
}

inline void split(const std::string&amp; s, std::string&amp; o1, std::string&amp; o2, std::string&amp; o3) {
    const unsigned int size_1 = std::stoi(s.substr(0, FIXED_FIELD_LENGTH));
    const unsigned int size_2 = std::stoi(s.substr(FIXED_FIELD_LENGTH, FIXED_FIELD_LENGTH));
    o1 = s.substr(2 * FIXED_FIELD_LENGTH, size_1); 
    o2 = s.substr(2 * FIXED_FIELD_LENGTH + size_1, size_2); 
    o3 = s.substr(2 * FIXED_FIELD_LENGTH + size_1 + size_2);
}
} // namespace MultipartSplitMerge 


At this point I wouldn't change the code anymore, unless I see via profiling that split or merge is a bottle-neck.

If you really notice that merge is a bottle-neck, you can use sprintf, but I guess that the compiler should optimize above code.