188

C# has a syntax feature where you can concatenate many data types together on 1 line.

string s = new String();
s += "Hello world, " + myInt + niceToSeeYouString;
s += someChar1 + interestingDecimal + someChar2;

What would be the equivalent in C++? As far as I can see, you'd have to do it all on separate lines as it doesn't support multiple strings/variables with the + operator. This is OK, but doesn't look as neat.

string s;
s += "Hello world, " + "nice to see you, " + "or not.";

The above code produces an error.

Gulzar
  • 23,452
  • 27
  • 113
  • 201
Nick Bolton
  • 38,276
  • 70
  • 174
  • 242
  • 5
    As explained elsewhere, this is not because "it doesn't support multiple strings/variables with the + operator" - but rather because you are trying to add `char *` pointers to each other. That's what generates the error - because summing pointers is nonsensical. As noted below, make at least the 1st operand into an `std::string`, and there's no error at all. – underscore_d Dec 04 '15 at 11:40
  • 1
    Which error was produced? – Wolf Sep 19 '17 at 09:28
  • Possible duplicate of [How to concatenate a std::string and an int?](https://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int) – vitaut Apr 29 '18 at 17:27

24 Answers24

287
#include <sstream>
#include <string>

std::stringstream ss;
ss << "Hello, world, " << myInt << niceToSeeYouString;
std::string s = ss.str();

Take a look at this Guru Of The Week article from Herb Sutter: The String Formatters of Manor Farm

Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193
94

In 5 years nobody has mentioned .append?

#include <string>

std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Michel
  • 1,395
  • 13
  • 14
  • Because it is cumbersome with comparison to just adding a text in one line. – Hi-Angel Feb 19 '15 at 11:46
  • 13
    `s.append("One"); s.append(" line");` – Jonny Aug 27 '15 at 08:23
  • 24
    @Jonny `s.append("One").append(" expression");` Maybe I should edit the original to use the return value this way? – Eponymous Sep 10 '15 at 16:25
  • @Eponymous This is not 1 line, as u forgot to declare the s. – slyy2048 Oct 17 '15 at 17:02
  • 7
    @SilverMöls The OP declares `s` on a different line in the equivalent C# code and in his non-compiling C++ code. His desired C++ is `s += "Hello world, " + "nice to see you, " + "or not.";` which can be written `s.append("Hello world, ").append("nice to see you, ").append("or not.");` – Eponymous Oct 19 '15 at 14:32
  • The accepted answer uses 3 lines. This solution can be written in 2. Shall we try to get back on topic please? – Michel Oct 19 '15 at 19:47
  • 6
    A major advantage of `append` is that it also works when the strings contain NUL characters. – John S. Mar 25 '18 at 15:35
79
s += "Hello world, " + "nice to see you, " + "or not.";

Those character array literals are not C++ std::strings - you need to convert them:

s += string("Hello world, ") + string("nice to see you, ") + string("or not.");

To convert ints (or any other streamable type) you can use a boost lexical_cast or provide your own function:

template <typename T>
string Str( const T & t ) {
   ostringstream os;
   os << t;
   return os.str();
}

You can now say things like:

string s = string("The meaning is ") + Str( 42 );
Community
  • 1
  • 1
  • 22
    You only need to explicitly convert the first one: s += string("Hello world,") + "nice to see you, " + "or not."; – Ferruccio Mar 19 '09 at 16:28
  • 10
    Yes, but I couldn't face explaining why! –  Mar 19 '09 at 16:31
  • 1
    boost::lexical_cast - nice and similar on your Str function:) – bayda Mar 19 '09 at 16:40
  • 6
    The concatenations done at the right of the constructor `string("Hello world")` are performed via `operator+()` defined in the class `string`. If there is no `string` object in the expression, the concatenation becomes a mere sum of char pointers `char*`. – davide Dec 12 '15 at 15:52
  • `error: use of undeclared identifier 's'` – Mote Zart Apr 04 '21 at 00:14
  • 1
    Instead of `string("Hello world, ")` you could use the c++ string literal `"Hello world, "s` – Noa Sakurajin Mar 07 '23 at 15:27
44

Your code can be written as1,

s = "Hello world," "nice to see you," "or not."

...but I doubt that's what you're looking for. In your case, you are probably looking for streams:

std::stringstream ss;
ss << "Hello world, " << 42 << "nice to see you.";
std::string s = ss.str();

1 "can be written as" : This only works for string literals. The concatenation is done by the compiler.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • 12
    Your 1st example is worth mentioning, but please also mention that it works only for "concatenating" literal strings (the compiler performs the concatenation itself). – j_random_hacker Mar 19 '09 at 16:32
  • The first example triggered an error for me if a string was previously declared as e.g. `const char smthg[] = "smthg"` :/ Is it a bug? – Hi-Angel Feb 19 '15 at 11:36
  • @Hi-Angel Unfortunately not, instead, you can `#define` your string to get around this, though this brings its own problems. – c z Jul 08 '20 at 12:32
33

Using C++14 user defined literals and std::to_string the code becomes easier.

using namespace std::literals::string_literals;
std::string str;
str += "Hello World, "s + "nice to see you, "s + "or not"s;
str += "Hello World, "s + std::to_string(my_int) + other_string;

Note that concatenating string literals can be done at compile time. Just remove the +.

str += "Hello World, " "nice to see you, " "or not";
Rapptz
  • 20,807
  • 5
  • 72
  • 86
28

In C++20 you can do:

auto s = std::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

Until std::format is available you could do the same with the {fmt} library:

auto s = fmt::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

Disclaimer: I'm the author of {fmt} and C++20 std::format.

vitaut
  • 49,672
  • 25
  • 199
  • 336
18

To offer a solution that is more one-line-ish: A function concat can be implemented to reduce the "classic" stringstream based solution to a single statement. It is based on variadic templates and perfect forwarding.


Usage:

std::string s = concat(someObject, " Hello, ", 42, " I concatenate", anyStreamableType);

Implementation:

void addToStream(std::ostringstream&)
{
}

template<typename T, typename... Args>
void addToStream(std::ostringstream& a_stream, T&& a_value, Args&&... a_args)
{
    a_stream << std::forward<T>(a_value);
    addToStream(a_stream, std::forward<Args>(a_args)...);
}

template<typename... Args>
std::string concat(Args&&... a_args)
{
    std::ostringstream s;
    addToStream(s, std::forward<Args>(a_args)...);
    return s.str();
}
SebastianK
  • 3,582
  • 3
  • 30
  • 48
  • wouldn't this become compile time bloat if there are several different of combinations in large code base. – Shital Shah Feb 08 '17 at 02:01
  • 1
    @ShitalShah not any more than just writing that stuff inline manually, since these helper functions will just get inlined anyway. – underscore_d Oct 13 '18 at 18:12
7

The actual problem was that concatenating string literals with + fails in C++:

string s;
s += "Hello world, " + "nice to see you, " + "or not.";
The above code produces an error.

In C++ (also in C), you concatenate string literals by just placing them right next to each other:

string s0 = "Hello world, " "nice to see you, " "or not.";
string s1 = "Hello world, " /*same*/ "nice to see you, " /*result*/ "or not.";
string s2 = 
    "Hello world, " /*line breaks in source code as well as*/ 
    "nice to see you, " /*comments don't matter*/ 
    "or not.";

This makes sense, if you generate code in macros:

#define TRACE(arg) cout << #arg ":" << (arg) << endl;

...a simple macro that can be used like this

int a = 5;
TRACE(a)
a += 7;
TRACE(a)
TRACE(a+7)
TRACE(17*11)

(live demo ...)

or, if you insist in using the + for string literals (as already suggested by underscore_d):

string s = string("Hello world, ")+"nice to see you, "+"or not.";

Another solution combines a string and a const char* for each concatenation step

string s;
s += "Hello world, "
s += "nice to see you, "
s += "or not.";
Wolf
  • 9,679
  • 7
  • 62
  • 108
  • I also use this technique a lot, but what if one or more variables are int/string ? .e.g. string s = "abc" "def" (int)y "ghi" (std::string)z "1234"; then, sprintf is still the best of worse solutions. – Bart Mensfort Nov 02 '17 at 10:41
  • @BartMensfort of course `sprintf` is an option, but there is also [std::stringstream](http://en.cppreference.com/w/cpp/io/basic_stringstream) which prevents problems with undersized buffers. – Wolf Nov 02 '17 at 14:53
7
auto s = string("one").append("two").append("three")
Shital Shah
  • 63,284
  • 17
  • 238
  • 185
7

boost::format

or std::stringstream

std::stringstream msg;
msg << "Hello world, " << myInt  << niceToSeeYouString;
msg.str(); // returns std::string object
Anirudh Ramanathan
  • 46,179
  • 22
  • 132
  • 191
bayda
  • 13,365
  • 8
  • 39
  • 48
3

If you write out the +=, it looks almost the same as C#

string s("Some initial data. "); int i = 5;
s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
Eponymous
  • 6,143
  • 4
  • 43
  • 43
3

As others said, the main problem with the OP code is that the operator + does not concatenate const char *; it works with std::string, though.

Here's another solution that uses C++11 lambdas and for_each and allows to provide a separator to separate the strings:

#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>

string join(const string& separator,
            const vector<string>& strings)
{
    if (strings.empty())
        return "";

    if (strings.size() == 1)
        return strings[0];

    stringstream ss;
    ss << strings[0];

    auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
    for_each(begin(strings) + 1, end(strings), aggregate);

    return ss.str();
}

Usage:

std::vector<std::string> strings { "a", "b", "c" };
std::string joinedStrings = join(", ", strings);

It seems to scale well (linearly), at least after a quick test on my computer; here's a quick test I've written:

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <chrono>

using namespace std;

string join(const string& separator,
            const vector<string>& strings)
{
    if (strings.empty())
        return "";

    if (strings.size() == 1)
        return strings[0];

    stringstream ss;
    ss << strings[0];

    auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
    for_each(begin(strings) + 1, end(strings), aggregate);

    return ss.str();
}

int main()
{
    const int reps = 1000;
    const string sep = ", ";
    auto generator = [](){return "abcde";};

    vector<string> strings10(10);
    generate(begin(strings10), end(strings10), generator);

    vector<string> strings100(100);
    generate(begin(strings100), end(strings100), generator);

    vector<string> strings1000(1000);
    generate(begin(strings1000), end(strings1000), generator);

    vector<string> strings10000(10000);
    generate(begin(strings10000), end(strings10000), generator);

    auto t1 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings10);
    }

    auto t2 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings100);
    }

    auto t3 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings1000);
    }

    auto t4 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings10000);
    }

    auto t5 = chrono::system_clock::now();

    auto d1 = chrono::duration_cast<chrono::milliseconds>(t2 - t1);
    auto d2 = chrono::duration_cast<chrono::milliseconds>(t3 - t2);
    auto d3 = chrono::duration_cast<chrono::milliseconds>(t4 - t3);
    auto d4 = chrono::duration_cast<chrono::milliseconds>(t5 - t4);

    cout << "join(10)   : " << d1.count() << endl;
    cout << "join(100)  : " << d2.count() << endl;
    cout << "join(1000) : " << d3.count() << endl;
    cout << "join(10000): " << d4.count() << endl;
}

Results (milliseconds):

join(10)   : 2
join(100)  : 10
join(1000) : 91
join(10000): 898
elnigno
  • 1,751
  • 14
  • 37
3

Here's the one-liner solution:

#include <iostream>
#include <string>

int main() {
  std::string s = std::string("Hi") + " there" + " friends";
  std::cout << s << std::endl;

  std::string r = std::string("Magic number: ") + std::to_string(13) + "!";
  std::cout << r << std::endl;

  return 0;
}

Although it's a tiny bit ugly, I think it's about as clean as you cat get in C++.

We are casting the first argument to a std::string and then using the (left to right) evaluation order of operator+ to ensure that its left operand is always a std::string. In this manner, we concatenate the std::string on the left with the const char * operand on the right and return another std::string, cascading the effect.

Note: there are a few options for the right operand, including const char *, std::string, and char.

It's up to you to decide whether the magic number is 13 or 6227020800.

Apollys supports Monica
  • 2,938
  • 1
  • 23
  • 33
  • Ah, you forget, @Apollys, the universal magic number is 42. :D – Mr.Zeus Dec 15 '18 at 17:51
  • 1
    It's strange to begin your answer with **"Here's the one-liner solution"**, as if you were the only one with a one-liner (which you are not; several, almost decade old answers already provided one-liners). The other answers just don't explicitly mention it because a one-liner was the very premise of the question, which is **"How do I concatenate multiple C++ strings on _one line?_"**. You should really turn down your arrogant tone a bit. Also, you are making performance tests and other "leet stuff", yet flush your output-buffer after every output... – Sebastian Mach Aug 31 '20 at 05:53
3

You would have to define operator+() for every data type you would want to concenate to the string, yet since operator<< is defined for most types, you should use std::stringstream.

Damn, beat by 50 seconds...

tstenner
  • 10,080
  • 10
  • 57
  • 92
  • 1
    You can't actually define new operators on built-in types like char and int. – Tyler McHenry Mar 19 '09 at 16:29
  • 1
    @TylerMcHenry Not that I'd recommend it in this case, but you certainly can: `std::string operator+(std::string s, int i){ return s+std::to_string(i); }` – Eponymous Oct 19 '15 at 14:37
2

Maybe you like my "Streamer" solution to really do it in one line:

#include <iostream>
#include <sstream>
using namespace std;

class Streamer // class for one line string generation
{
public:

    Streamer& clear() // clear content
    {
        ss.str(""); // set to empty string
        ss.clear(); // clear error flags
        return *this;
    }

    template <typename T>
    friend Streamer& operator<<(Streamer& streamer,T str); // add to streamer

    string str() // get current string
    { return ss.str();}

private:
    stringstream ss;
};

template <typename T>
Streamer& operator<<(Streamer& streamer,T str)
{ streamer.ss<<str;return streamer;}

Streamer streamer; // make this a global variable


class MyTestClass // just a test class
{
public:
    MyTestClass() : data(0.12345){}
    friend ostream& operator<<(ostream& os,const MyTestClass& myClass);
private:
    double data;
};

ostream& operator<<(ostream& os,const MyTestClass& myClass) // print test class
{ return os<<myClass.data;}


int main()
{
    int i=0;
    string s1=(streamer.clear()<<"foo"<<"bar"<<"test").str();                      // test strings
    string s2=(streamer.clear()<<"i:"<<i++<<" "<<i++<<" "<<i++<<" "<<0.666).str(); // test numbers
    string s3=(streamer.clear()<<"test class:"<<MyTestClass()).str();              // test with test class
    cout<<"s1: '"<<s1<<"'"<<endl;
    cout<<"s2: '"<<s2<<"'"<<endl;
    cout<<"s3: '"<<s3<<"'"<<endl;
}
bterwijn
  • 69
  • 2
1

You may use this header for this regard: https://github.com/theypsilon/concat

using namespace concat;

assert(concat(1,2,3,4,5) == "12345");

Under the hood you will be using a std::ostringstream.

José Manuel
  • 1,056
  • 1
  • 9
  • 15
1

If you are willing to use c++11 you can utilize user-defined string literals and define two function templates that overload the plus operator for a std::string object and any other object. The only pitfall is not to overload the plus operators of std::string, otherwise the compiler doesn't know which operator to use. You can do this by using the template std::enable_if from type_traits. After that strings behave just like in Java or C#. See my example implementation for details.

Main code

#include <iostream>
#include "c_sharp_strings.hpp"

using namespace std;

int main()
{
    int i = 0;
    float f = 0.4;
    double d = 1.3e-2;
    string s;
    s += "Hello world, "_ + "nice to see you. "_ + i
            + " "_ + 47 + " "_ + f + ',' + d;
    cout << s << endl;
    return 0;
}

File c_sharp_strings.hpp

Include this header file in all all places where you want to have these strings.

#ifndef C_SHARP_STRING_H_INCLUDED
#define C_SHARP_STRING_H_INCLUDED

#include <type_traits>
#include <string>

inline std::string operator "" _(const char a[], long unsigned int i)
{
    return std::string(a);
}

template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
                        !std::is_same<char, T>::value &&
                        !std::is_same<const char*, T>::value, std::string>::type
operator+ (std::string s, T i)
{
    return s + std::to_string(i);
}

template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
                        !std::is_same<char, T>::value &&
                        !std::is_same<const char*, T>::value, std::string>::type
operator+ (T i, std::string s)
{
    return std::to_string(i) + s;
}

#endif // C_SHARP_STRING_H_INCLUDED
Scindix
  • 1,254
  • 2
  • 15
  • 32
1

Something like this works for me

namespace detail {
    void concat_impl(std::ostream&) { /* do nothing */ }

    template<typename T, typename ...Args>
    void concat_impl(std::ostream& os, const T& t, Args&&... args)
    {
        os << t;
        concat_impl(os, std::forward<Args>(args)...);
    }
} /* namespace detail */

template<typename ...Args>
std::string concat(Args&&... args)
{
    std::ostringstream os;
    detail::concat_impl(os, std::forward<Args>(args)...);
    return os.str();
}
// ...
std::string s{"Hello World, "};
s = concat(s, myInt, niceToSeeYouString, myChar, myFoo);
smoothware
  • 898
  • 7
  • 19
1

Based on above solutions I made a class var_string for my project to make life easy. Examples:

var_string x("abc %d %s", 123, "def");
std::string y = (std::string)x;
const char *z = x.c_str();

The class itself:

#include <stdlib.h>
#include <stdarg.h>

class var_string
{
public:
    var_string(const char *cmd, ...)
    {
        va_list args;
        va_start(args, cmd);
        vsnprintf(buffer, sizeof(buffer) - 1, cmd, args);
    }

    ~var_string() {}

    operator std::string()
    {
        return std::string(buffer);
    }

    operator char*()
    {
        return buffer;
    }

    const char *c_str()
    {
        return buffer;
    }

    int system()
    {
        return ::system(buffer);
    }
private:
    char buffer[4096];
};

Still wondering if there will be something better in C++ ?

Bart Mensfort
  • 995
  • 11
  • 21
1

In c11:

void printMessage(std::string&& message) {
    std::cout << message << std::endl;
    return message;
}

this allow you to create function call like this:

printMessage("message number : " + std::to_string(id));

will print : message number : 10

devcodexyz
  • 65
  • 7
0

you can also "extend" the string class and choose the operator you prefer ( <<, &, |, etc ...)

Here is the code using operator<< to show there is no conflict with streams

note: if you uncomment s1.reserve(30), there is only 3 new() operator requests (1 for s1, 1 for s2, 1 for reserve ; you can't reserve at constructor time unfortunately); without reserve, s1 has to request more memory as it grows, so it depends on your compiler implementation grow factor (mine seems to be 1.5, 5 new() calls in this example)

namespace perso {
class string:public std::string {
public:
    string(): std::string(){}

    template<typename T>
    string(const T v): std::string(v) {}

    template<typename T>
    string& operator<<(const T s){
        *this+=s;
        return *this;
    }
};
}

using namespace std;

int main()
{
    using string = perso::string;
    string s1, s2="she";
    //s1.reserve(30);
    s1 << "no " << "sunshine when " << s2 << '\'' << 's' << " gone";
    cout << "Aint't "<< s1 << " ..." <<  endl;

    return 0;
}
ddy
  • 59
  • 2
0

Stringstream with a simple preproccessor macro using a lambda function seems nice:

#include <sstream>
#define make_string(args) []{std::stringstream ss; ss << args; return ss;}() 

and then

auto str = make_string("hello" << " there" << 10 << '$');
asikorski
  • 882
  • 6
  • 20
-1

This works for me:

#include <iostream>

using namespace std;

#define CONCAT2(a,b)     string(a)+string(b)
#define CONCAT3(a,b,c)   string(a)+string(b)+string(c)
#define CONCAT4(a,b,c,d) string(a)+string(b)+string(c)+string(d)

#define HOMEDIR "c:\\example"

int main()
{

    const char* filename = "myfile";

    string path = CONCAT4(HOMEDIR,"\\",filename,".txt");

    cout << path;
    return 0;
}

Output:

c:\example\myfile.txt
erik80
  • 31
  • 1
  • 12
    A kitten cries every time someone uses macros for something more complex than code guards or constants :P – Rui Marques May 21 '14 at 15:08
  • 1
    Beside unhappy kittens: For each argument a string object is created which is not necessary. – SebastianK Jun 02 '14 at 13:56
  • 2
    downvoted, since using macros is definitely a bad solution – dhaumann Nov 18 '15 at 08:37
  • this would make me flinch in horror even for C, but in C++, it's diabolical. @RuiMarques: in which situations are macros better for constants than a `const` or (if zero storage is a requirement) an `enum` would be? – underscore_d Dec 04 '15 at 11:43
  • @underscore_d interesting question but I have no answer for it. Maybe the answer is none. – Rui Marques Dec 04 '15 at 13:51
  • Yeah, I suspect it is :-) – underscore_d Dec 04 '15 at 13:57
  • @underscore_d Absolutely incorrect. Look at Boost.Preprocessor or Boost.Config for why; there are things that cannot be done in the language without macros. – Alice May 02 '16 at 15:07
  • @Alice Surprise: you've not actually paid any attention to context, namely the bit where we were talking about why a macro would be better than a `const[expr]` variable (or in older dialects, the `enum` hack). So I haven't called macros useless, and no counterargument is required. – underscore_d May 02 '16 at 15:20
  • @underscore_d Surprise! Using Boost.Preprocessor to generate a compile time constant is (sometimes) better than using a constexpr or an enum hack, if the computations needed are substantial. – Alice May 02 '16 at 16:12
  • @Alice To clarify, I was only talking about the naïve implementation `#define SOME_MACRO 42`. However, `constexpr` is vastly improved in C++14 fwiw. Not that I specifically doubt it, but is there an easy example of where macros beat it for calculating constants? – underscore_d May 02 '16 at 16:14
  • @underscore_d Something that requires a lot of recursion; template instantiation is bounded, and `constexpr` is also be bounded by compiler related limits. File inclusion based macros can exceed these limits in many cases, at the cost of a lot of HDD access. – Alice May 03 '16 at 14:18
  • @Alice Interesting. Are macros specifically defined as not being limited? – underscore_d May 05 '16 at 15:25
  • @underscore_d No, it's just how they are implemented; no one really intended for such macros to exist, just like no one intended for template metaprogramming to exist. It was "discovered" not "invented". – Alice May 05 '16 at 18:46
-1

Have you tried to avoid the +=? instead use var = var + ... it worked for me.

#include <iostream.h> // for string

string myName = "";
int _age = 30;
myName = myName + "Vincent" + "Thorpe" + 30 + " " + 2019;
vincent thorpe
  • 171
  • 1
  • 10