Append int to std::string - c++

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.

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.

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.

The correct way to add to your string would be
std::string s;
s += std::to_string(2);
s = s + std::to_string(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.

Related

std::string difference in output when use c+=expression and c=c+expression

In the below code
#include<iostream>
#include<string>
using namespace std;
int main()
{
string a,c="!";
cin>>a;
int l=a.size();
for(int i=0;i<l;i++)
{
c=c+"#"+a[i];
}
cout<<c;
}
If I replace c=c+"#"+a[i] with c+="#"+a[i] i get unexpected output.
Output in the second case is !boxboxboxbox irrespective of input on https://www.onlinegdb.com/ .
On "dev c++" the output is -
But a += b is equivalent to a = a + b . Then what is the reason for the difference in output?
Given c+="#"+a[i];, "#"+a[i] is evaluated at first. "#" is of type const char[2] and could decay to pointer as const char*, a[i] is of type char which is an integral type, then "#"+a[i] just performs pointer arithmetic and won't concatenate strings as you expected. (And the result of pointer arithmetic might get out of the bound of the array and then leads to UB.)
On the other hand, in c=c+"#"+a[i];, c+"#" is evaluated at first, it appends "#" on c and returns a new std::string (by operator+ for std::string), on which a[i] is appended and the result is assigned to c.
But a += b is equivalent to a = a + b
If you put b in integration, i.e. add parentheses as ("#"+a[i]), then both c+=("#"+a[i]); and c=c+("#"+a[i]); yields the same result, even it's not what you expected.
With c=c+"#"+a[i] all the operators in the right of the expression are the same so the expression is processed from left to right, the first element is a std::string to which a const char* is added creating a new std::string then add a char creating another std::string which is finally assigned to c.
With c+="#"+a[i] the right of the expression starts with a const char* to which you add a char, this invokes pointer arithmetic producing an invalid address which is then appended to the string c which is undefined behaviour. To fix it you have to force the first argument to be a std::string: c+=std::string("#")+a[i]
Fundamentally because C++ started its life as "C with classes". Over the years a bunch of new functionality was added and some rules were tightened but C++'s background as an extended C is still clearly visible. In particular.
The language doesn't make a proper distinction between characters and integers. Type "char" is simply the smallest integer type in the language.
A regular string literal evaluates to a pointer the first character in a constant array containing a null-terminated string, not to a modern string type.
std::string (strictly the std::basic_string template but lets ignore that detail for now) does it's best to help you. It defines sensible overloads for (again ignoring the detail of rvalue references).
std::string + std::string
std::string + char*
std::string + char
char* + std::string
char + std::string
But it can't do anything about operators where neither argument is a std::string. Those work in C++ in the same way they work in C.
char* + char* --> error
char + char --> integer addition
char* + char --> pointer arithmetic
char + char* --> pointer arithmetic
The result of this is that order of operations becomes very important.
c=c+"#"+a[i]; is equivalent to c=((c+"#")+a[i]);. This works fine, in the innermost operation one argument is a std::string so the overloaded operators do the right thing and concatenate the arguments to produce another std::string. The same applies when we concatenate the result of that innermost operation to the a[i]
c+="#"+a[i]; is functionally equivalent* to c=(c+("#"+a[i])); so now we are trying to use the + operator between a string literal which evaluates to a char * and an operation which evaluates to a char. So we add the character code for the character at a[i] to the pointer to the string "#".
since "#" is a rather short string, this will almost certainly result in a pointer that is past the end of the string. This is undefined behaviour by the language spec.
I would guess that "!boxboxbox" is a sandbox error from onlinegdb. It has detected your code doing something it shouldn't and refused to let it go ahead.
Many compilers/linkers put different string data together, so on a regular compiler displaying (part of) another string from the executable (or libraries that it uses) is a likely outcome of running off the end of a string.
C++11 did add support for std::string literals, so one fix could be to add
using namespace std::string_literals;
Then change "#" to "#"s
* Note that in general with overloaded operators in c++ "+" and "+=" are separate operators and nothing forces the implementer of the class to make them functionally equivalent. Sane class designers generally will though.
Also += may be more efficient as it may be able to perform the concatenation in-place rather than creating a new string.

static_cast vs. direct call to conversion operator?

Consider the following class, just as a simple example:
#include <iostream>
#include <string>
using namespace std;
class point {
public:
int _x{ 0 };
int _y{ 0 };
point() {}
point(int x, int y) : _x{ x }, _y{ y } {}
operator string() const
{ return '[' + to_string(_x) + ',' + to_string(_y) + ']'; }
friend ostream& operator<<(ostream& os, const point& p) {
// Which one? Why?
os << static_cast<string>(p); // Option 1
os << p.operator string(); // Option 2
return os;
}
};
Should one call a conversion operator directly, or rather just call static_cast and let that do the job?
Those two lines will pretty much do exactly the same thing (which is to call the conversion operator), there's no real difference between their behavior as far as I can tell. So the real question here is whether that's true or not. Even though these seem the same to me, there could still be subtle differences that one might fail to pick up on.
So are there any practical differences between those approaches (including ones that might not apply to this example), other than the fact that the syntax for them different? Which one should be preferred and why?
So are there any practical differences between those approaches
In this case, not that I know of, behaviour wise.
(including ones that might not apply to this example)
static_cast<X>(instance_of_Y) would also allow conversion if X has a converting constructor for the type Y. An explicit call to (possibly non-existent) conversion operator of Y could not use the mentioned converting constructor. In this case of course, std::string does not have a converting constructor for point.
So, the cast is more generic and that is what I would prefer in general. Also "convert this object to type string" is more meaningful than "call the operator string()". But if for some very strange reason you want to avoid the converting constructor, then explicit call to conversion operator would achieve that.
No you never need to call the conversion operator member function directly.
If you use an instance of the class where a std::string object is expected then the conversion operator will be called automatically by the compiler, as will it if you use e.g. static_cast to cast an instance to std::string.
Simple and stupid example:
void print_string(std::string const& s)
{
std::cout << s << '\n';
}
int main()
{
point p(1, 2);
print_string(p); // Will call the conversion operator
print_string(static_cast<std::string>(p)); // Will call the conversion operator too
}
The closest to call the function directly you will ever need is using something like static_cast.
In your specific case, with the output operator, then you should use static_cast. The reason is semantic and for future readers and maintainers (which might include yourself) of the code.
It will of course work to call the conversion member function directly (your option 2) but it loses the semantic information that says "here I'm converting the object to a string".
If the only use of the conversion operator is to use in the output operator, you might as well create a (private) function that takes the output stream reference as an argument, and writes directly to the stream, and call that function from the output operator.

How to choose which operator overload in C++?

I'm learning C++ and creating a simple class, tstring, defined by:
n the number of characters in a string;
ntotal the total number of characters in the tstring;
p, pointer to a zone which contains the data.
Hence, this class is composed by a static part (n, ntotal and p) and by a dynamic part (the data itself).
I created three constructors :
one without parameters;
one copy constructor;
one constructor which takes a C-string parameter.
I would like to be able to concatenate one tstring object with one C string, using for example the instructions :
tstring x("Hello), y;
y = x + " World";
I think I should overload the + operator. However I don't understand which way I should use to overload it between internal way :
tstring tstring::operator+(char string[])
or external way (with setter and getter methods)
tstring operator+ (const tstring& myTstring, const char* string)
Can someone explain me the difference and the best way to adopt?
Thank you in advance!
In this case, you probably should consider concatenation of two tstrings first. Having this, you may decide to allow your tstring(const char *) constructor to deal with conversion for you. This way you will get concatenation with C-strings free.
Then you'll notice that tstring + char [] does not yield the same result as char [] + tstring. That is because first will call your converting constructor and second will not find any matching operator.
As having asymmetrical operator+ is counterintuitive, you should also provide non-member operator+(const char *, tstring const &) that will forward its arguments to its in-class sibling.

Is it safe to use double quote to initialize string as using generic algorithm in C++?

I am studying the C++ Primer 5th edition. On page 479, it mentions that
string sum = accumulate(v.cbegin(), v.cend(), ""); is not correct since no + on const char* for "";
The type of v is a sequential container such as a vector.
I found that using double quotes to create a string is dangerous, especially for the condition that you use generic algorithm, since it requires the objects' defined operator.
But I can't figure out what the description no + on const char* means?
Will it call the constructor string (const char* s); which is defined in c-string?
I think that's what the compiler does as it interprets the string which are initialized with quotes in C++.
Should we create a string with double quotes? It will cause overriding on operators failed.
What it's saying is that you can't add pointers to [const] char to each other, nor can you add a char to a char [const] * (well, technically, you can add a char to a char *, but it won't do what you want--it'll basically treat the char * as if it points to the beginning of an array, and treat the char as an index into that array, so the result will be a char * offset by some distance from the original, where the distance is equal to the encoded value of the char you added).
std::accumulate deduces the type it should use for the summation from the type of the value you pass as the third parameter. It tries to add things using that type, then when it's done, it attempts to return that value (then, in this case the assignment would attempt to convert char const * to std::string). That final conversion would work fine, but the intermediate additions would not.
To make it work, you need to pass an actual std::string as the initial value:
string sum = accumulate(v.cbegin(), v.cend(), string());
This way it will deduce the summation type as std::string, so it'll be adding items to a string instead of trying to add to a char const *. If you're compiler is new enough, you could get the same effect with:
string sum = accumulate(v.cbegin(), v.cend(), ""s);
C++11 added a "user defined literal" feature that allows a suffix like this to invoke a constructor for an object type. C++14 added a number of predefined suffixes like this, including 2 that use the s suffix like this--this one (for a string literal) creates a std::string. The other (for integers) creates a timing value used with the std::chrono timing classes/functions, so 3s means "three seconds".
I found that using double quotes to create string is dangerous
This is nonsense. There's no way in which it is "dangerous" to use string literals. In fact, using double quotes is pretty much the only way to create string literals, short of initializing each character separately in an array. And that is way more broken.
no + on const char* for "";
I have no idea what this means, either. I doubt it's a quote out of the book or your compiler's error messages.
I think what it's trying to say is that there is no + operator defined for string literals. That is true. They are just pointers to an array of characters. You can only overload operators for class objects. That's the advantage of std::string—you can concatenate string objects using the + operator.
If you want a string object, you will need to create one:
std::string myString("Hello");
You can concatenate two of those:
std::string myOtherString("World");
std::cout << (myString + myOtherString) << std::endl;
To answer your questions, if you want a string object, you should create one. You do not, however, always need a string object. Sometimes a C-style string literal is perfectly acceptable. It is foolish to make "rules" about what you should or should not do. It is actually quite rare that a normal application concatenates strings like this.
const char* is just a pointer to a C style array, if you try and use the + operator on 2 of them it won't compile.
String literal "" has type const char [1]. Passed to the algorithm it is adjusted to type const char *
Inside the algorithm there is used operator +. However this operator is not defined for adding two objects of type const char *. So the compiler shall issue an error.
If inside the body of the algorithm there is used an object of type std::string then the problem will be with the assignment statement because there is no assignment operator for objects of type const char * that accepts objects of type std::string as the right operands.
So it seems you should write the call of the algorithm the following way
string sum = accumulate(v.cbegin(), v.cend(), string() );
And the author of the book meant this when it said that "no + on const char*
That is if there are two pointers for example
const char *p = "Hello ";
const char *q = "World";
you may not write
q + p

C++ ambiguity overloading problem

I have a class called FileProc that runs File IO operations. In one instance I have declared two functions (which are sub-functions to operator= functions), both decisively different:
const bool WriteAmount(const std::string &Arr, const long S)
{
/* Do some string conversion */
return true;
}
const bool WriteAmount(const char Arr[], unsigned int S)
{
/* Do some string conversion */
return true;
}
If I make a call with a 'char string' to WriteAmount, it reports an ambiguity error - saying it is confused between WriteAmount for char string and WriteAmount for std::string. I know what is occurring under the hood - it's attempting to using the std::string constructor to implicitly convert the char string into a std::string. But I don't want this to occur in the instance of WriteAmount (IE I don't want any implicit conversion occurring within the functions - given each one is optimised to each role).
My question is, for consistency, without changing the function format (IE not changing number of arguments or what order they appear in) and without altering the standard library, is there anyway to prevent implicit conversion in the functions in question?
I forgot to add, preferably without typecasting, as this will be tedious on function calls and not user friendly.
You get the ambiguity because your second parameter is different. Trying to call it with long x = ...; WriteAmount("foo", x) will raise an ambiguity because it matches the second argument better with the first overload, but the first argument is matched better with the second overload.
Make the second parameter have the same type in both cases and you will get rid of the ambiguity, as then the second argument is matched equally worse/good for both overloads, and the first argument will be matched better with the second overload.
Can't you change the second argument and cast it to unsigned int ? It should not be able to use the first function call. I have not coded in C++ for ages..