I am aware about the +
operator to join 2 std::string
objects. And this method to join multiple strings. But I am wondering about the performance. This answer tells why +
operator is inefficient as compared to a method (in Python). Is there a similar function to join multiple strings in C++ standard library?

- 930
- 13
- 24

- 961
- 1
- 8
- 15
-
1There is no performance difference, it is quite literally syntactic sugar. – Austin Brunkhorst Dec 14 '17 at 02:03
-
2Possible duplicate of [Efficient string concatenation in C++](https://stackoverflow.com/questions/611263/efficient-string-concatenation-in-c) – Dec 14 '17 at 02:06
-
You could use a stream but that's at best slightly faster – Jake Freeman Dec 14 '17 at 02:07
-
1@AustinBrunkhorst that isn't true, is it - doing string a = b + c + d + e; will invoke `operator+` multiple times, returning a temporary each time. A variadic function that did concat(a, b, c, d) could compute the length needed for the result, allocate one array, and perform the concatenation. `stringstream` is, of course, much more general. – sfjac Dec 14 '17 at 02:08
-
@sfjac of course, but I don't think that's what this question is asking. If it is, then it should be clarified. – Austin Brunkhorst Dec 14 '17 at 02:11
-
@AustinBrunkhorst: If you look at the linked python question, I think its quite clear that that is what this question is asking. Since the answer there states that there is nothing wrong with concatenating 2 strings with operator+, but that it might be less efficient for more than 2. – Benjamin Lindley Dec 14 '17 at 02:14
-
@BenjaminLindley I can see where my confusion is. I'll update my answer to address his question better. – Austin Brunkhorst Dec 14 '17 at 02:17
-
@BenjaminLindley updated my answer. – Austin Brunkhorst Dec 14 '17 at 02:53
3 Answers
There is no performance difference between an operator overload and method call -- depending on the calling context. At the point you should be concerned about this, you're micro optimizing.
Here's an abstract example demonstrating the concept.
class MyString
{
public:
// assume this class is implemented
std::string operator+(const std::string &rhs) const
{
return concatenate(rhs);
}
std::string concatenate(const std::string &rhs) const
{
// some implementation
}
};
MyString str1, str2;
// we can use the operator overload
std::string option1 = str1 + str2;
// or we can call a method
std::string option2 = str1.concatenate(str2);
Operator overloads exist (for the most part) to avoid typing out lengthy method calls like the latter example. It makes code more clean and concise.
If you were specifically talking about the performance of more than 2 strings, this is a different scenario. It's more performant to batch objects into one method call, as it avoids constructing more temporary objects than necessary. You won't be able to do this without a new data structure to do the heavy lifting for you.
Using the class above, we'll look at concatenating a bunch of objects together using the +
operator in one expression.
std::string bigConcatenation = str1 + str2 + str1 + str2 + str1;
Firstly, you probably wouldn't be doing this if you were concerned about performance in the first place. That said, here's a pretty decent approximation of what would be happening (assuming no optimizations done by the compiler).
std::string bigConcatenation = str1;
bigConcatenation = bigConcatenation + str2;
bigConcatenation = bigConcatenation + str1;
bigConcatenation = bigConcatenation + str2;
bigConcatenation = bigConcatenation + str1;
The reason why this is not ideal, is each assignment creates a temporary object, adds them together, then assigns the result back to bigConcatenation
.
Without using any extra containers, according to this answer, the most performant way of doing this would be something like this (hint: no temporaries are created in the process).
std::string bigConcatenation = str1;
bigConcatenation += str2;
bigConcatenation += str1;
bigConcatenation += str2;
bigConcatenation += str1;

- 20,704
- 6
- 47
- 61
-
1The last example is certainly *more* efficient, but to have a good claim on *most* efficient `bigConcatenation` should probably be default-initialized, then `bigConcatenation.reserve(str1.size() * 3 + str2.size() * 2)`, then a series of `operator+=` calls. – bcrist Dec 14 '17 at 05:43
-
Concatenating strings is rarely enough a bottleneck in C++ that almost nobody ever attempts to optimize it much.
If you get into a situation where it's honestly important to do so, it's fairly easy to avoid the problem though, with fairly minimal change, so a = b + c + d + e;
stays a = b + c + d + e;
instead of something like a.concat(b, c, d, e);
(which, I'd add, is actually quite non-trivial to make work well). Depending on the types involved, you may need to add code to convert the first item in the list to the right type. For the obvious example, we can't overload operator+
to work on string literals, so if you wanted to concatenate string literals together, you'd have to explicitly convert the first to some other type.
The "trick" is in something called expression templates (except that in this case it doesn't really even need to be a template). What you do is create a string builder object that overloads its operator+
to just store pointers/references to the source strings. That, in turn, overloads a conversion to std::string
that adds up the lengths of all the source strings, concatenates them together into an std::string
, and finally returns that entire string. For example, code can look like this:
#include <string>
#include <iostream>
#include <vector>
#include <numeric>
#include <cstring>
namespace string {
class ref;
class builder {
std::vector<ref const *> strings;
public:
builder(ref const &s) { strings.push_back(&s); }
builder & operator+(ref const &s) {
strings.push_back(&s);
return *this;
}
operator std::string() const;
};
class ref {
char const *data;
size_t N;
public:
ref(char const *s) : data(s), N(std::strlen(s)) {}
ref(char const *s, size_t N) : data(s), N(N-1) {}
size_t length() const { return N; }
operator char const *() const { return data; }
builder operator+(ref const &other) {
return builder(*this) + other;
}
};
builder::operator std::string() const {
size_t length = std::accumulate(strings.begin(), strings.end(),
size_t(),
[](size_t a, auto s) { return a + s->length(); });
std::string ret;
ret.reserve(length);
for (auto s : strings)
ret.append(*s);
return ret;
}
}
string::ref operator "" _s(char const *s, unsigned long N) { return string::ref(s, N); }
int main() {
std::string a = "b"_s + "c" + "d" + "e";
std::cout << a << "\n";
}
Note that as the code is written above, you (at least theoretically) gain some run-time efficiency if you converted all the literals to string_ref
s up-front:
std::string a = "b"_s + "c"_s + "d"_s + "e"_s;
This way, the compiler measures the length of each literal at compile time, and passes the length directly to the user-defined literal operator. If you just pass string literals directly, the code uses std::strlen
to measure the length at run time instead.

- 476,176
- 80
- 629
- 1,111
you can use a stringstream remember the to add the directive #include
into the header file or source file of your class. Whichever one has your method definitions.