14

As I know that C++ only allows to add 2 strings together, i.e: s = s1 + s2

But how can I add many strings together? Like:

s = s1 + s2 + s3 + s4 + ... + sn
6
  • 9
    Why do you think that this doesn't work? Commented Feb 20, 2010 at 3:19
  • 1
    are you using the string class? Commented Feb 20, 2010 at 3:20
  • yes, i notice it works only when I use string class. but i want to do something like this in C++ #define St "blah3" s = s1 + "blah1" + "blah2" + St Commented Feb 20, 2010 at 3:33
  • 2
    @tsubasa: Well, you can't. If you know two literals at compile time, simply placing them adjacent to one another will allow them to concatenate in the preprocessor. i.e., "asd" "123" becomes "asd123". But run-time addition of strings requires you use the string class. Commented Feb 20, 2010 at 3:37
  • 1
    @Billy: Well, it won't add the pointer values per se. It'll try but fail to compile. Commented Feb 20, 2010 at 4:22

6 Answers 6

15

If you're trying to append string objects of std::string class, this should work.

string s1 = "string1";
string s2 = "string2";
string s3 = "string3";

string s = s1 + s2 + s3;

OR

string s = string("s1") + string("s2") + string("s3") ...
Sign up to request clarification or add additional context in comments.

6 Comments

+1 - Just keep in mind when you use operator+(std::basic_string<t>, std::basic_string<t>) you will incur exponential time in C++03. Using the std::basic_string<t>::append member function takes only linear time in comparison.
I think string s = string("s1") + string("s2") + string("s3") ... is something that I'm looking for. I wonder why it works?
Because you are when you use string(x), you invoke a constructor for the standard string class on x. You create a temporary string object which can then participate with it's operator+. (This sort-of works like a form of cast)
@tsubasa: It's also enough if you just make sure the first item is a string, so string("s1") + "s2" + "s3" will also work. The reason is that normal string constants like "s1" are of type const char*, and you can't just add such pointers together. string objects on the other hand know how to add a const char* to form a new string. (Also: Nothing will use exponential time here, so don't worry too much about that)
@BillyONeal: are you sure it's exponential time? Look O(N^2) to me.
|
8

First of all, you can do the +sn thing just fine. Though it's going to take exponential quadradic(see comments) time assuming you're using std::basic_string<t> strings on C++03.

You can use the std::basic_string<t>::append in concert with std::basic_string<t>::reserve to concatenate your string in O(n) time.

EDIT: For example

string a;
//either
a.append(s1).append(s2).append(s3);
//or
a.append("I'm a string!").append("I am another string!");

10 Comments

It's not going to take exponential time, only quadratic. append will be faster, but in general quadratic anyway because it needs to reallocate form time to time. In the very most cases both methods won't be slow enough to be noticeable, though.
@BillyONeal: Why would that be exponential? The sum you mention is equal to (0*N + N) + (1*N + N) + (2*N + N) + ... + ((K-1)*N + N) which equals (1+2+...+(K-1)) * N + K*N = (K*(K-1)/2) * N + K*N = ((K^2 + K) / 2) * N = O(K^2 * N). So it is quadratic in the number of parts K. In the number of characters in each part, N, it is linear.
About append(): I seem to have missed that you mentioned reserve(). Used with reserve() it is indeed linear.
@sth Even without reserving enough space, append takes linear amortized time which is way faster than quadratic
@BillyONeal You could claim that it is exponential where the exponent in question happens to be 2 (which admittedly still isn't great :P)
|
5
s = s1 + s2 + s3 + .. + sn;

will work although it could create a lot of temporaries (a good optimizing compiler should help) because it will effectively be interpreted as:

string tmp1 = s1 + s2;
string tmp2 = tmp1 + s3;
string tmp3 = tmp2 + s4;
...
s = tmpn + sn;

An alternate way that is guaranteed not to create temporaries is:

s = s1;
s += s2;
s += s3;
...
s += sn;

1 Comment

Your example is not quite the same tmp1 must be copy construct. It would be the same if tmp1 was 'string const&'. Though I do have to admit that RVO would probably eliminate the copy.
3

std::ostringstream is build for that, see example here. It's easy:

std::ostringstream out;
out << "a" << "b" << "c" << .... << "z";
std::string str( out.str());

5 Comments

I would point out that using stringstream for appending strings is slower than just appending to a string. stringstream is great for converting things to strings, but if all you're doing is constructing strings from strings, then it's the less efficient way to do it. append() and += are the way to go.
Actually, I wrote a test program for it recently and found that it took about 2 1/2 times as long to put strings together with stringstream than appending directly to a string - and that's without getting the string out of the stringstream when you're done. That particular figure likely isn't generally accurate with varying circumstances and all that, but it was quite clear that constructing streams with stringstream is significantly slower than just appending to a string.
Well I did some tests and found that though slightly slower, it was only 20% slower. Taking a string and adding 10 string with 10 characters onto it. Reset and repeat this a million times. The difference is 2 seconds (on my machine). Hardly even worth noticing as any processor stalls will knock that difference away immediately. The win for me on this is the lower overhead on the memory management system. Less calls to allocate space as the string stream has a relatively large buffer.
This is probably due to a particular implementation of internal string buffer management (exponential growth?) and string stream buffer management. Neither are guaranteed to behave one way or the other.
@Nikolai True. If you really care and you want to be sure of which is faster for what you're doing, you need to profile it for the particular program that you're working on with the particular implementation that you're using. However, the general indications are that stringstream is likely to be slower, so I'd say to use string's append() or += unless you need stringstream for its particular abilities, or you really care about performance and have profiled your code and found that stringstream is better in your particular case.
1

Use a template to add strings, char* and char's to form a string

strlen:-

#include <iostream>
#include <cstring>

// it_pair to wrap a pair of iterators for a for(:) loop
template<typename IT> 
class it_pair
    {
    IT b;
    IT e;
public:
    auto begin() const
        {
        return b;
        }
    auto end() const
        {
        return e;
        }
    };

// string length
template<typename S> auto strlen(const S& s) -> decltype(s.size())
    {
    return s.size();
    }

auto strlen(char c) -> size_t
    {
    return 1u;
    }

auto strlen(const std::initializer_list<char>& il) -> size_t
    {
    return il.size();
    }

template<typename IT>
auto strlen(const it_pair<IT>& p)
    {
    auto len = size_t{};
    for(const auto& s:p)
        len += strlen(s);
    return len;
    }

template<typename S, typename ...SS> auto strlen(S s, SS&... ss) -> size_t
    {
    return strlen(s) + strlen(ss...);
    }

appending strings

// terminate recursion
template<typename TA, typename TB>
void append(TA& a, TB& b)
    {
    a.append(b);
    }

// special case for a character
template<>
void append<std::string, const char>(std::string& a, const char& b)
    {
    a.append(1, b);
    }

// special case for a collection of strings
template<typename TA, typename TB>
void append(TA& a, const it_pair<TB>& p)
    {
    for(const auto& x: p)
        a.append(x);
    }

// recursion append
template<typename TA, typename TB, typename ...TT>
void append(TA& a, TB& b, TT&... tt)
    {
    append(a, b);
    append(a, tt...);
    }

template<typename ...TT>
std::string string_add(const TT& ... tt)
    {
    std::string s;
    s.reserve(strlen(tt...));
    append(s, tt...);
    return s;
    }

template<typename IT>
auto make_it_pair(IT b, IT e)
    {
    return it_pair<IT>{b, e};
    }

template<typename T>
auto make_it_pair(const T& t)
    {
    using namespace std;
    return make_it_pair(cbegin(t), cend(t));
    }

main example

int main()
    {
    const char * s[] = {"vw", "xyz"};
    std::vector<std::string> v{"l", "mn", "opqr"};
    std::string a("a");
    std::string b("bc");
    std::string c("def");
    std::cout << string_add(a, b+c, "ghij", make_it_pair(v), 'k', make_it_pair(s));
    }

1 Comment

You should create a proposal for standardisation :)
0

If you want to be able to do this

  • Efficiently, i.e without incurring quadratic time
  • Without overwriting the original variable
  • Without creating temporary variables

Then this will do the job

auto s = std::string(s1).append(s2).append(s3).append(sn);  

And if you like things nicely formatted

auto s = std::string(s1).append(s2)
                        .append(s3)
                        .append(sn)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.