Concatenation of std::string and int leads to a shift. Why? - c++

Today I was surprised when trying to concatenate an std::string with an int. Consider the following MWE:
#include <iostream>
#include <string>
void print(const std::string& text)
{
std::cout << "The string is: " << text << ".\n";
}
int main()
{
print("iteration_" + 1);
return 0;
}
Instead of printing
The string is: iteration_1.
which I would expect, it prints
The string is: teration_.
What exactly is going on in the background? Does the string for some reason get converted into char[] or something of the sort? The documentation of operator+ does not list any with an std::string and int.
And what is the proper way of concatenating an std::string with a number? Do I really have to throw them both into an std::stringstream or convert the number into std::string explicitely with std::to_string()?

Does the string for some reason get converted into char[]
Actually it is the other way around. "iteration_" is a char[11] which decays to a const char* when you add 1. Incrementing the pointer by one makes it point to the next character in the string. This is then used to construct a temporary std::string that contains all but the first character.
The documentation you link is for operator+ of std::string, but to use that you need a std::string first.

This line is the problem:
print("iteration_" + 1);
The string literal is decaying to a char*. You are adding 1 to this char*, moving it to the next character.
If you wanted to add the string "1" to the end of your literal, a fairly simple way is to pass the string literal to the std::string constructor and convert the 1 to a string manually. For example:
print(std::string("iteration_") + std::to_string(1));

"iteration_" is not std::string, but const char[]. Which decays to const char*, and "iteration_" + 1 just performs pointer arithmetic and move the pointer pointing to the next char (i.e. 't'), then you got the c-style string "teration_".
You can use std::to_string to convert int to std::string, then concatenate them. e.g.
print("iteration_" + std::to_string(1));
For this case std::operator+(std::basic_string) is called and the 1st argument "iteration_" is converted to std::string implicitly and then passed to operator+, then the concatenated std::string is passed to print.
LIVE

If you try to use the following:
std::string str = "iteration" + 1;
compiler will throw the warning:
warning: adding 'int' to a string does not append to the string
[-Wstring-plus-int]
It is because you are incrementing the pointer to "iteration" string by 1 which means that now "teration" string is being assigned to str variable.
The proper way of concatenating would be:
std::string str = "iteration" + std::to_string(1);

The expression "iteration_" + 1 is a const char[11] literal added to the int 1.
In that expression, "iteration_" decays to a const char* pointer to the first element of the array. + 1 then takes place in pointer arithmetic on that pointer. The entire expression evaluates to a const char* type (pointing to the first t) which is a valid NUL-terminated input to a std::string constructor! (The anonymous temporary std::string binds to the const std::string& function parameter.)
This is completely valid C++ and can occasionally be put to good use.
If you want to treat + as a concatenation, then
print("iteration_" + std::to_string(1));
is one way.

Related

Difference between passing string S and string S[ ] in C++

void str(string S[])
{
S=S+"jg";
cout<<S;
}
In the above code, it throws me an error. I understand because I am passing the pointer S. But, when I remove the square brackets off, it doesn't throw me an error. What is the reason?
If string is really std::string then it have an overloaded operator for + that appends a std::string to another std::string.
And character literals can decay to a const char * which is implicitly convertible to std::string.
So without the square brackets you append two strings, and
S = S + "jg";
is really equivalent to
S = operator+(S, std::string("jg")); // Add S and "jg", assign result back to S
Which is equivalent to
S += "jg";
As for the reason it doesn't work with the square brackets, it's because then S is indeed a pointer, it's a pointer to string (i.e. string*).
Character literals are really arrays of constant characters, and as any array they decay to pointers to their first element.
That means the expression S + "jg" is trying to add values of the types string* and char const*, which is not possible and you get an error because of that.
if I understand you correctly
/*
accepts array of strings, but you must specify the size also
other wise function won't know the size of array;
example:
std::string[] arrOfStrings = {"venkat", "chary", "padala"};
str( arrOfStrings );
with size parameter;
str( arrOfStrings, 3 );
*/
void str( std::string s[] )
{
}
/*
accepts std::string
example: str( "abcdefg" );
*/
void str( std::string s )
{
}

I'm getting a compile time error when appending a string to a const char* in C++?

so I'm trying to convert an integer value into a string and then append the string to an already existing const char*. But as mentioned, I'm getting this error:
error: expression must have integral or unscoped enum type.
objDim += objDimStr.c_str();
Any ideas why?
My code is as follows:
const char* objDim = "The object dimension is: ";
int objDimension = geo->Dimension();
std::string objDimStr = std::to_string(objDimension);
objDim += objDimStr.c_str();
objDim is a pointer. When you add to the pointer, you will add to the pointer itself and not append anything to the data it points to.
And you can not add everything to a pointer, basically only integer values. And adding two pointers like you do makes no sense at all (which is why it's not allowed).
If you want to be able to append strings, then use std::string for all strings.
Besides, that you need to use the const qualifier should also give some hints about that what the pointer is pointing to is constant and therefore can't be modified (including added to).
And in this case you don't need any of the temporary variable objDimension or objDimStr (unless you use them later in the code):
std::string objDim = "The object dimension is: " + std::to_string(geo->Dimension());
Here the addition works because there is an overloaded operator+ function that takes the correct arguments in the correct order, and returns a new std::string object.
I assume you were expecting to get a concatenation of strings. No, it's not how it works. objDim is but a pointer to a non-modifiable memory area that contains characters. The only possible application of operator += to it that would compile is if you were incrementing it by a few characters:
objDim += 11;
std::cout << objDim << '\n'; // prints "dimension is: ";
To concatenate strings you can:
Either assign the result to a string object:
std::string result = objDim + objDimStr;
(Note the absence of c_str anywhere: you cannot sum two pointers, but there's an overload that can prepend a char pointer to a std::string.)
Or give proper type to objDim;
std::string objDim{"The object dimension is: "};
objDim += objDimStr;
objDim is not an std::string but a pointer, so your concatenation operator (+) which is defined for std::string will not work on it.
Define it as an std::string and you will be able to use it to concatenate other std::strings to it.
objDim += objDimStr.c_str();
Here you add one pointer to other instead of concatenating the string. You need use std::string to concatenate, for example:
const char* objDim = "The object dimension is: ";
int objDimension = geo->Dimension();
std::string objDimStr = objDim;
objDimStr += std::to_string(objDimension);
std::cout << objDimStr << std::endl;

Assignment in strings

I'm having trouble understanding some particular behaviour of assignment in strings.
//method 1
std::string s;
s+='a' //This works perfectly
but
//method2
std::string s;
s="" + 'a';//This gives unexpected value
Why 2nd method gives unexpected value ? From what I've read string default constructor initialise string variable as empty string, if no constructor is specified. And s+='a' should be same as s=s+a. So why isn't the method 2 same as method 1?
And one more query on the same topic , if we can't initialise a string with char literal then how can we assign a char literal to it?
std::string s2='a'//gives error while compiling
whereas
std::string s2;
s2='a'//works perfect
From what I understand is we cannot initialise a string variable by char variable because string constructor needs argument of the type(const char *). Why is there not any such restriction while assigning?
For your first query ,
method 1 works perfectly cause in this method you are adding string object type and char literal .
and s+='a' , is indeed same as s=s+'a'
focus on the fact that s is string object type rather than string literal.
In the 2nd method , you are adding string literal
and char literal . Focus on the difference between the two , In first method there is string object you can add string or char literals to string object type,its one of the features provided by string object type . But you cant add simply add the literals with each other.In c++ , however "StringLiteral1" "StringLiteral2" , will result in the concatenation of the two string literals.
for 2nd query,
Initialisation is not the same as assignment , string object default constructor takes const char * to initialise . Assignment is a completely differenet story(if not,someone please do correct me ).
"" is a string literal of type const char[], and you are adding the string literal, i.e. the pointer to the first element, '\0', to another character. This will naturally give you something else then you expected.
If you want it to be the same as s += 'a', you'll need to use a std::string literal: s += ""s + 'a';. This works, as ""s is an empty std::string, and you just add another character to it.
When you write s="" + 'a'; Remember that "" is not a std::string but a const char*. And const char* doesn't have a predefined concatenation operator. That's why you are having an unexpected behavior instead of concatenation.

How can I convert const char* to string and then back to char*?

I'm just starting c++ and am having difficulty understanding const char*. I'm trying to convert the input in the method to string, and then change the strings to add hyphens where I want and ultimately take that string and convert it back to char* to return. So far when I try this it gives me a bus error 10.
char* getHyphen(const char* input){
string vowels [12] = {"A","E","I","O","U","Y","a","e","i","o","u","y"};
//convert char* to string
string a;
int i = 0;
while(input != '\0'){
a += input[i];
input++;
i++;
}
//convert a string to char*
return NULL;
}
A: The std::string class has a constructor that takes a char const*, so you simply create an instance to do your conversion.
B: Instances of std::string have a c_str() member function that returns a char const* that you can use to convert back to char const*.
auto my_cstr = "Hello"; // A
std::string s(my_cstr); // A
// ... modify 's' ...
auto back_to_cstr = s.c_str(); // B
First of all, you don't need all of that code to construct a std::string from the input. You can just use:
string a(input);
As far as returning a new char*, you can use:
return strdup(a.c_str()); // strdup is a non-standard function but it
// can be easily implemented if necessary.
Make sure to deallocate the returned value.
It will be better to just return a std::string so the users of your function don't have to worry about memory allocation/deallocation.
std::string getHyphen(const char* input){
Don't use char*. Use std::string, like all other here are telling you. This will eliminate all such problems.
However, for the sake of completeness and because you want to understand the background, let's analyse what is going on.
while(input != '\0'){
You probably mean:
while(*input != '\0') {
Your code compares the input pointer itself to \0, i.e. it checks for a null-pointer, which is due to the unfortunate automatic conversion from a \0 char. If you tried to compare with, say, 'x' or 'a', then you would get a compilation error instead of runtime crashes.
You want to dereference the pointer via *input to get to the char pointed to.
a += input[i];
input++;
i++;
This will also not work. You increment the input pointer, yet with [i] you advance even further. For example, if input has been incremented three times, then input[3] will be the 7th character of the original array passed into the function, not the 4th one. This eventually results in undefined behaviour when you leave the bounds of the array. Undefined behaviour can also be the "bus error 10" you mention.
Replace with:
a += *input;
input++;
i++;
(Actually, now that i is not used any longer, you can remove it altogether.)
And let me repeat it once again: Do not use char*. Use std::string.
Change your function declaration from
char* getHyphen(const char* input)
to
auto hyphenated( string const& input )
-> string
and avoid all the problems of conversion to char const* and back.
That said, you can construct a std::string from a char_const* as follows:
string( "Blah" )
and you get back a temporary char const* by using the c_str method.
Do note that the result of c_str is only valid as long as the original string instance exists and is not modified. For example, applying c_str to a local string and returning that result, yields Undefined Behavior and is not a good idea. If you absolutely must return a char* or char const*, allocate an array with new and copy the string data over with strcpy, like this: return strcpy( new char[s.length()+1], s.c_str() ), where the +1 is to accomodate a terminating zero-byte.

Trying to add a string object to an integer

This is probably a very basic question for which I have been searching on google for the last 20 mins. I am not sure if i am phrasing it correctly, but I am not getting an explanation that I understand.
Basically, I have a string object and when I add an integer value x, it shortens the string by x characters.
Here is the code:
#include <iostream>
#include <string>
void Print::print(std::string str)
{
std::cout << str << std::endl;
}
print("formatString:" + 5);
The output is: tString:
Now i realise that the above is incorrect and during my search I have found ways correct the behaviour, but I haven’t found what is actually happening internally for me to get the above result.
Thanks
The answer is simple: Pointer arithmetic.
Your string literal (array of const char including implicit 0-terminator), decays to a const char* on use, which you increment and pass to your print()-function, thus invoking the std::string-constructor for string literals.
So, yes, you start with a string object (0-terminated array of const char), but not a std::string object.
Basically, I have a string object
No, you do not have a string object. "formatString:" is not a std::string, but a "string" literal. It is in fact a const char*. A const char* has a operator + defined that takes an integer and advances the value of the pointer with a number of positions. In your case it's 5.
To get a compiler error you'd have to wrap the literal in a std::string.
print(std::string("formatString:") + 5);
"formatString:" is a string literal that has type const char[14] That is it is an array of const char with size equal to 14 (the array includes the terminating zero).
In expressions like this
"formatString:" + 5
the array is implicitly converted to a pointer to its first element. So if for example const char *p denotes this pointer then the expression looks as
p + 5
The result of the expression is a pointer that points to the element of the array with index 5. That is there is used the pointer arithmetic.
P + 5 points to the first symbol of string "tString"
And this expression is used by the constructor of class std::string.
Examine the following,
#include <iostream>
void print(std::string str)
{
std::cout << str << std::endl;
}
int main(int argc, char* argv[])
{
//following two lines created implicitly by the compiler
const char* pstr = "formatString";
std::string tmp(pstr + 5); //string c-tor: string (const char* s);
// now tmp: --> "tString"
print(tmp);
return 0;
}
pstr is a pointer and you are doing pointer arithmetic when you use + operation.
Note:Compiler may create different internal structure, but it is a instructive way to think the above two lines.