42

I tried two different ways to append an int to a std::string, and to my surprise, I got different results:

#include <string>

int main()
{
    std::string s;
    s += 2;     // compiles correctly
    s = s + 2;  // compiler error

    return 0;
}

Why does it compile and work correctly when I use the += operator, but fail when I use the + operator?

I don't think the question is like How to concatenate a std::string and an int?

In that question,no answer uses += operator.And the difference between += and + operator of std::string is the key to solve my doubt.

Frankly,the question is a good example for explaining why c++ is so difficult to master.

navylover
  • 12,383
  • 5
  • 28
  • 41
  • 9
    You are not adding a string representation of the integer `2` in either case. You want to use `std::to_string`. – Jesper Juhl Aug 04 '17 at 11:23
  • 1
    I'm not sure but I think the `operator+` is expecting a `string` not an `int` – Bo Halim Aug 04 '17 at 11:25
  • 13
    You cannot append an `int` to a string. What you can do is append the **text** that represents an integer value to a string. Don't lose sight of that conversion step. – Pete Becker Aug 04 '17 at 11:32
  • 3
    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) – Cole Tobin Aug 06 '17 at 06:34

4 Answers4

52

TL;DR operator+= is a class member function in class string, while operator+ is a template function.

The standard class template<typename CharT> basic_string<CharT> has overloaded function basic_string& operator+=(CharT), and string is just basic_string<char>.

As values that fits in a lower type can be automatically cast into that type, in expression s += 2, the 2 is not treated as int, but char instead. It has exactly the same effect as s += '\x02'. A char with ASCII code 2 (STX) is appended, not the character '2' (with ASCII value 50, or 0x32).

However, string does not have an overloaded member function like string operator+(int), s + 2 is not a valid expression, thus throws an error during compilation. (More below)

You can use operator+ function in string in these ways:

s = s + char(2); // or (char)2
s = s + std::string(2);
s = s + std::to_string(2); // C++11 and above only

For people concerned about why 2 isn't automatically cast to char with operator+,

template <typename CharT>
  basic_string<CharT>
  operator+(const basic_string<CharT>& lhs, CharT rhs);

The above is the prototype[note] for the plus operator in s + 2, and because it's a template function, it is requiring an implementation of both operator+<char> and operator+<int>, which is conflicting. For details, see Why isn't automatic downcasting applied to template functions?

Meanwhile, the prototype of operator+= is:

template <typename CharT>
class basic_string{
    basic_string&
      operator+=(CharT _c);
};

You see, no template here (it's a class member function), so the compiler deduces that type CharT is char from class implementation, and int(2) is automatically cast into char(2).


Note: Unnecessary code is stripped when copying from C++ standard include source. That includes typename 2 and 3 (Traits and Allocator) for template class "basic_string", and unnecessary underscores, in order to improve readability.

iBug
  • 35,554
  • 7
  • 89
  • 134
45

s += 2; is not doing what you think it's doing. It calls the overloaded += operator to a char. It does not append the character '2', but rather the character with value 2, and the result will depend on the encoding used on your platform.

There is no operator overload defined to allow s + 2 to compile1. Hence the error.

The solution in both cases is to use std::to_string(2) rather than the int literal 2.


1 Essentially the reason is because operator+= is not a template function, but std::operator+ is, and overload resolution will favour a non-template function over a template one.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • What's about: `string operator+ (const string& lhs, char rhs);` as he uses `s + 2`? Why isn't the compiler using that `operator+`? – Andre Kampling Aug 04 '17 at 11:26
  • One of the other answers goes into more detail. – Bathsheba Aug 04 '17 at 11:30
  • 2
    But I didn't see why the compiler call `string& operator+= (char c);` but not `string operator+ (const string& lhs, char rhs);`? – Andre Kampling Aug 04 '17 at 11:32
  • 1
    Methinks that's a (good) question in itself (it's to do with the fact that the compiler must try to convert 2 to a `std::string`, which it can't). Why not ask it? – Bathsheba Aug 04 '17 at 11:32
  • 1
    @AndreKampling: I capitulated and added more detail. – Bathsheba Aug 04 '17 at 11:44
  • 2 is of type int. It won't be implicitly converted to char because it would lose precision. – Sergei Aug 04 '17 at 11:59
  • 2
    The fact that `s += 2` compiles really feels like a defect – Tristan Brindle Aug 04 '17 at 12:02
  • 2
    See also this question that refers to this one about [Why isn't automatic downcasting applied to template functions?](https://stackoverflow.com/questions/45506372/why-isnt-automatic-downcasting-applied-to-template-functions). – Andre Kampling Aug 04 '17 at 12:35
  • @AndreKampling Lol... That question is asked by me :) – iBug Aug 05 '17 at 02:41
  • "overload resolution will favour a non-template function over a template one" - that wouldn't explain why no function at all is found for `operator+`. – M.M Aug 15 '17 at 05:49
14

The correct way to add to your string would be

std::string s;
s += std::to_string(2);
s = s + std::to_string(2);
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • 3
    Note that [std::to_string](http://de.cppreference.com/w/cpp/string/basic_string/to_string) is only available on C++11 and higher. – Andre Kampling Aug 04 '17 at 11:23
  • 5
    @AndreKampling It's 2017. Unless a question is specifically tagged or otherwise mentions C++03, it's fair to assume C++11 support is available. – aschepler Aug 05 '17 at 11:31
  • 1
    The generally recommend way to output an object to a string, is to use an std::ostringstream, stream the object to that, and then use the .str() function to get the std::string from the std::ostringstream. This should work for all C++ dialects, and all objects which have an insertion operator. – CSM Aug 05 '17 at 19:21
  • 6
    @CSM "generally recommended"? And why would that be? The function `std::to_string` is perfectly reasonable in this context, and was created for just this purpose. – Cory Kramer Aug 05 '17 at 19:50
2

While @CoryKramer answer gives you the correct way to add an integer to a string, it doesn't explain why the instruction s = s + 2 does not compile.

The difference between the two instruction is that in the first one you use the std::string 's += operator while in the second instruction, the compiler tries to cast 2 to a string.

There is no implicit conversion between int and std::string. however, you can cast an int to char, so this is why s += 2works.

Xatyrian
  • 1,364
  • 8
  • 26
  • `s = s + 2` doesn't crash. It fails to compile. – Bathsheba Aug 04 '17 at 11:29
  • `s + 2` does not compile, but `s + '\x02'` does. How do you explain? – iBug Aug 04 '17 at 12:15
  • @iBug I believe its related to '\x02' being a char, and the + operator knowing how to add a char to a string since a string is essentially a fancy array of chars. – CCD Sep 08 '19 at 21:31