I am reading Accelerated C++ by Koenig. He writes that "the new idea is that we can use + to concatenate a string and a string literal - or, for that matter, two strings (but not two string literals).
Fine, this makes sense I suppose. Now onto two separate exercises meant to illuminate this .
Are the following definitions valid?
const string hello = "Hello";
const string message = hello + ",world" + "!";
Now, I tried to execute the above and it worked! So I was happy.
Then I tried to do the next exercise;
const string exclam = "!";
const string message = "Hello" + ",world" + exclam;
This did not work. Now I understand it has something to do with the fact that you cannot concatenate two string literals, but I don't understand the semantic difference between why I managed to get the first example to work (isn't ",world" and "!" two string literals? Shouldn't this not have worked?) but not the second.
const string message = "Hello" + ",world" + exclam;
The + operator has left-to-right associativity, so the equivalent parenthesized expression is:
const string message = (("Hello" + ",world") + exclam);
As you can see, the two string literals "Hello" and ",world" are "added" first, hence the error.
One of the first two strings being concatenated must be a std::string object:
const string message = string("Hello") + ",world" + exclam;
Alternatively, you can force the second + to be evaluated first by parenthesizing that part of the expression:
const string message = "Hello" + (",world" + exclam);
It makes sense that your first example (hello + ",world" + "!") works because the std::string (hello) is one of the arguments to the leftmost +. That + is evaluated, the result is a std::string object with the concatenated string, and that resulting std::string is then concatenated with the "!".
As for why you can't concatenate two string literals using +, it is because a string literal is just an array of characters (a const char [N] where N is the length of the string plus one, for the null terminator). When you use an array in most contexts, it is converted into a pointer to its initial element.
So, when you try to do "Hello" + ",world", what you're really trying to do is add two const char*s together, which isn't possible (what would it mean to add two pointers together?) and if it was it wouldn't do what you wanted it to do.
Note that you can concatenate string literals by placing them next to each other; for example, the following two are equivalent:
"Hello" ",world"
"Hello,world"
This is useful if you have a long string literal that you want to break up onto multiple lines. They have to be string literals, though: this won't work with const char* pointers or const char[N] arrays.
You should always pay attention to types.
Although they all seem like strings, "Hello" and ",world" are literals.
And in your example, exclam is a std::string object.
C++ has an operator overload that takes a std::string object and adds another string to it. When you concatenate a std::string object with a literal it will make the appropriate casting for the literal.
But if you try to concatenate two literals, the compiler won't be able to find an operator that takes two literals.
Since C++14 you can use two real string literals:
const string hello = "Hello"s;
const string message = hello + ",world"s + "!"s;
or
const string exclam = "!"s;
const string message = "Hello"s + ",world"s + exclam;
Your second example does not work because there is no operator + for two string literals. Note that a string literal is not of type string, but instead is of type const char *. Your second example will work if you revise it like this:
const string message = string("Hello") + ",world" + exclam;
The difference between a string (or to be precise, std::string) and a character literal is that for the latter there is no + operator defined. This is why the second example fails.
In the first case, the compiler can find a suitable operator+ with the first argument being a string and the second a character literal (const char*) so it used that. The result of that operation is again a string, so it repeats the same trick when adding "!" to it.
In case 1, because of order of operations you get:
(hello + ", world") + "!" which resolves to hello + "!" and finally to hello
In case 2, as James noted, you get:
("Hello" + ", world") + exclam which is the concat of 2 string literals.
Hope it's clear :)
if we write
string s = "hello" + "world!";
RHS has following type
const char [6] + const char [7]
Now both are built in data types.
ie, they are not std::string types any more.
So, now operator overloading of built in types
as defined by compiler applies.
ie - no more operator + overloaded by std::string.
now let us turn to how compiler overloads
binary operator for two operands of const char * type.
it turns out, compiler did not overload for this case, as it is meaning less.
ie, adding two 'const char *' is semantically wrong as result would be another const char * in run time.
There can be many reason why above does not make sense.
Hence over all, there is one generic rule for any operator overloading. it is :
overloading any operator when all operands of that operator are built-in only. Compiler designers would take of such cases. In our exact question, std::string can't overload two 'const literals' because of this rule, and compiler choose to not to implement the + binary operator for its meaninglessness.
if we like the string literal form and we can a "s" operator as below.
std::string p = "hello"s + "world!"s;
just suffix with s, the meaning changes.
(s overloaded operator)
Related
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.
This question already has answers here:
String concatenation
(5 answers)
Concatenate two string literals
(7 answers)
Closed 9 years ago.
I am a novice in C++ and i am referring Accelerated C++. While trying one of its exercise questions which says:
Are the following definitions valid? Why or why not?
const std::string exclam = "!";
const std::string message = "Hello" + ", world" + exclam;
When i tried & executed the program i am getting an error as:
invalid operands of types to binary operator +.
But the following code works perfectly fine:
const std::string hello = "Hello";
const std::string message = hello + ", world" + "!";
I am not clear with its execution! why this concatenation in the first case not working?
Thanks!I am using DEV C++.
In the first expression
"Hello" + ", world"
the compiler needs to find a suitable function such as operator+(const char *, const char *).
No such function exists, so it cannot compile.
Conversely,
hello + ", world"
is looking for one matching operator+(const std::string&, const char*), and that overload does exist (it is provided by std::string).
Note that you even if you could write your own overload:
std::string operator+ (const char *left, const char *right)
{
return std::string(left) + right;
}
(and you can't, as Praetorian points out) it wouldn't be a good idea.
Firstly, with primitive arguments, you'd lose ADL (ie, if the standard library put the operator in namespace std, it wouldn't normally be visible outside).
Secondly, if you have other libraries with their own string representations (eg. pre-STL stuff like RogueWave, or Qt, etc. etc.) they'd be equally justified in providing an overload for their string type, and the compiler would have no way to choose between them.
It's because "Hello" is not, when the compiler reads it, a std::string, but const char * - which means you can't use it for +.
You can trivial fix it with:
const std::string message = std::string("Hello") + ...
"Hello" is not a string, in the sense that it's not an object of type std::string. It's a string literal, which is an array of characters. You can't concatenate two literals with +, but you can concatenate a std::string with an array (and vice versa).
"Hello" + ", world" + exclam is equivalent to ("Hello" + ", world") + exclam, and so doesn't work because it tries to concatenate two literals. However, you could concatenate them without the + operator: "Hello" ", world" + exclam
hello + ", world" + "!"; is equivalent to (hello + ", world") + "!". It concatenates a std::string with a literal; the result is a new std::string which is then concatenated with the second literal. Both concatenations are allowed.
The reason is that C++ is a member of a language family that has slowly evolved over the last half century or so, and still has vestigial remnants of ancient languages around the edges.
"Hello" and ", world" in the first example are const char* objects. There is no defined way to add two character arrays together due to the nature of pointers, although logically it seems fine.
In the second example, hello is a std::string objects which has a + operator defined so that when you write:
hello + ", world"
it creates a new std::string object containing both pieces of content.
I noticed that when I initialized a string, the compiler reported an error that I was not expecting.
For example:
#include <iostream>
#include <string>
using namespace std;
int main() {
string s1 = "Hello", s2 = "World!"; // ok
string s3 = s1 + ", " + "World!"; // ok
string s4 = "Hello" + ", " + s2; // error
cout << s1 + " " + s2 << endl; //ok
return 0;
}
For me, if s3 worked just fine, s4 should do the same.
Why do I get that error? What is the difference between these two initialization strings (s3 and s4)?
"Hello" is not a std::string (rather it is a const char[6], while ", " is a const char[3]) and the + operator for std::strings does not apply.
This is a minor inconvenience of C++ and stems from its C ancestry. It means that in concatenating strings via + you must ensure that at least one of its two operands actually is a std::string, as in
auto s = std::string{"Hello"} + ", " + "World";
where the first + has a std::string as its first operand and hence produces a std::string, so that the second + also has a std::string as its first operand (since + is processed from left to right).
Edit1 prompted by the comment by T.C., I mention that C-string literals are automatically concatenated if only separated by white space:
std::string s = "Hello" ", " "World";
This behaviour too is inherited from C: the preprocessor renders above code to
std::string s = "Hello, World";
before the compiler proper processes it (to be more precise, string concatenation takes place in phase 6 of translation, just before compilation). This is in fact the most simple and hence most convenient way to concatenate raw string literals. But note that I had to declare the type of s, since auto deduction would have given a const char*.
Edit2 prompted by the comment by PaperBirdMaster, I mention that since C++14, you can directly form std::string literals by simply adding s after the string, if you pull in the associated operator""s (or surrounding namespace).
using std::literals::operator""s; // pull in required operator
const auto s = "Hello"s + ", " + "World"; // std::string
See this post as to why the required operator""s is hidden in the nested namespace. Note also that alternatively to using std::literals::operator""s; you may pull in the surrounding namespace: either of the following declarations will do.
using namespace std::string_literals;
using namespace std::literals::string_literals;
using namespace std::literals;
which are not as bad (and damned) as plain using namespace std;.
Strings and C-strings
In C++, strings are usually represented as either std::string or a C-string which has type char[N], where N is the number of characters in the C-string plus one for the termination character (or null character) \0.
When passing a string literal as, e.g., "Hello" to a function, you are actually passing a char[6], which will decay into a pointer char*, as C++ does not allow you to pass arrays by value. Additionally, C++ does not allow non-const pointers to string literals, and the parameter type must therefore become const char*.
Concatenation
std::string has existing overloads for the binary addition operator which allows you to concatenate two std::string objects into one, as well as concatenate a std::string object with a C-string.
std::string s1 = "Hello";
std::string s2 = "World";
// Uses 'std::string operator+(const std::string&, const std::string&)'.
std::string s3 = s1 + s2;
// Uses 'std::string operator+(const std::string&, const char*)'.
std::string s4 = s1 + "World";
// Also ok.
std::string s4 = "Hello" + s2;
There is however, no overload for concatenating two C-strings.
// Error. No overload for 'std::string operator+(const char*, const char*)' found.
std::string s5 = "Hello" + "World";
From left to right
Addition is performed left to right in C++, and hence, long concatenation expressions need to start with a std::string object.
std::string s6 = "Hello" + ", " + s2; // Error. Addition is performed left-to-right and
// you can't concatenate two C-strings.
std::string s7 = s1 + ", " + "World"; // Ok. 's1 + ", "' returns a new temporary
// 'std::string' object that "World" is concat'ed to.
A simple solution
Using C++14 standard literals, you can create std::string objects from literal expressions (note the trailing s in the literal expression).
using namespace std::literals;
std::string s8 = "Hello"s + ", " + "World"; // Ok
Alternatively you can use the fact that the preprocessor will automatically concatenate literal strings separated with whitespace.
std::string s9 = "Hello" ", " "World"; // Ok
But then, you could just as well skip the whitespace altogether and use a single string literal.
The following line attempts to add 2 pointers together.
string s4 = "Hello" + ", " + s2; // error
"Hello" is a const char[6] which decays to const char* and ", " is a const char[3] which decays to const char*.
You are trying to assign the addition of two pointers to a std::string, which is not valid. Furthermore, the addition of two pointers is not valid either.
As other people have said, this doesn't have anything to do with the overloads of operator+ of the std::string class, as the following code would also not compile for the same reason:
string s4 = "Hello" + ", ";
I have added some clarifications provided by user #dyp.
You can fix your problem by using the C++ Standard Library "user-defined" string literal. The functionality is provided by std::literals::string_literals::operator""s which is found in the <string> header. You might have to use a using-directive or -declaration; this was not required on my compiler and including the <string> header was enough. Example:
string s4 = "Hello"s + ", "s + s2;
This is the equivalent of:
string s4 = std::string("Hello") + std::string(", ") + s2;
When you place an instance of std::string as the first operand, the rest of the expression will use that type, because of left to right associativity for operator+ will make sure that an instance of std::string is returned as the left-most operand every time, instead of const char*. That is why you have no error on line:
string s3 = s1 + ", " + "World!";
That line is equivalent to:
string s3 = ((s1 + ", ") + "World!");
The first thing to understand is that due to its ancestry in C, a string literal like "Hello" in C++ is not actually of type string. Its type is actually an array of characters instead.
So the difference between s3 and s4 is that when you do the s3 construction, the first operation done is s1 + ", ". s1 is of type string, so the + operator provided by the string component of the standard library is used, and returns a new string to be added to "World!".
Now for s4 the first operation to be performed is "Hello" + ", " or the addition of two string literals. This operation would actually been see to the compiler as adding two character arrays together, a capability not provided by the language, thus resulting in the error you see.
The simplest fix is to just not use + for string literals, just put them all together in the same string. Alternately you could create a string from the first literal and let the operation work from there (std::string("Hello") + ", " + s2)
The + operator is overloaded in std::string.
Operator overloads are applied to the operands, so
s1 + ... works by calling the overloaded + operator on s1.
Literal "Hello" is a const char * and so is ",". const char * does not have an overloaded + operator, hence the error.
Try string("Hello") + ... and it will work.
Thanks for the correction #interjay
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Concatenate two string literals
Why doesn't this work ?
const std::string exclam = "!";
const std::string message = "Hello" + ", world" + exclam;
BUT this works fine
const std::string exclam = "!";
const std::string message = exclam +
"Hello" + ", world" ;
Please explain to me.
Thanks
The reason is that there is no operator+ for adding two string literals, and it is not needed. Your first example works if you just remove the +.
const std::string message = "Hello" ", world" + exclam;
because preprocessor compiler magic*) will add the two adjacent literals together.
The second example works because std::string does have an operator+ that adds a string literal. The result is another string, that can concatenate the next literal.
*) Translation phase 6 - Adjacent string literal tokens are concatenated.
Because expression "Hello" + ", world" doesn't involve any std::string, but two const char[] arguments. And there is no operator+ with that signature. You have to convert one of those to std::string first:
const std::string message = std::string("Hello") + ", world" + exclam;
std::string has a + operator which is what is being used in the second example.
const char * does not have that operator, which is being used in the first example.
It's down to associativity.
The second case begins (from the left) evaluation with std::string which is concatenated with operator+. The first case begins with const char * and no concatenation operator+ exists for that.
The "const string doesn't work if appended at the end" is a red herring. This doesn't work, either:
const std::string message = "Hello" + ", world";
The reason this doesn't work has been explained in the other answers.
#include <iostream>
int main()
{
const std::string exclam = "!";
const std::string message = "Hello" + ", world" + exclam;
std::cout << message;
return 0;
}
Why does this code not work? Error returned:
error: invalid operands of types `const char[6]' and `const char[8]' to binary `operator+'
Thanks in advance!
EDIT:
Thanks for all the answers. This is my first time on the site and I am astonished at the number of elaborate explanations in such a short time interval.
Regarding the actual question. How come this works then:
const std::string hello = "Hello";
const std::string message = hello + ", world" + "!";
Is it because ", world" and afterwards "!" get concatenated with variable hello (which is defined)?
Because in C++, string literals (like "Hello" are not of type std::string. They are plain char arrays, or C-style strings.
So for the line const std::string message = "Hello" + ", world" + exclam;,the types the compiler has to work with are:
const std::string message = const char[6] + const char[8] + std::string;
and given the associativity of +, the operations it has to perform are:
const std::string message = ((const char[6] + const char[8]) + std::string);
That is, the left-most addition must be evaluated first, and the result passed to the rightmost addition.
So the compiler tries to evaluate const char[6] + const char[8].
There is no addition defined for arrays. Arrays are implicitly converted to pointers, but this doesn't help the compiler. That just means it ends up with const char* + const char*, and no addition is defined for pointers either.
At this point, it doesn't know that you want the result to be converted to a std::string.
However, in your second example:
const std::string hello = "Hello";
const std::string message = hello + ", world" + "!";
it works, because the operations the compiler would see were std::string + const char[8] + const char[2]. Here, the first addition can be converted to std::string + const char*, and here the addition operator is defined, and returns a std::string. So the compiler has successfully figured out the first addition, and since the result was a string, the second addition looks like this: std::string + const char[2], and like before, this isn't possible, but the array can be converted to a pointer, and then the compiler is able to find an addition operator that works, again resulting in a std::string.
"Hello" + ", world"
Since these are c-style strings, you cannot append them with +. You can append a std::string to a c-style string, but not 2 c-style strings this way, instead add a std::string() constructor around one of them to make a temporary, ie:
"Hello" + std::string(", world")
C++ doesn't do many of the automatic 'behind the scenes' conversations of other OO languages.
As Doug said you need to do std::string("hello") + std::string(" world"), the language doesn't do this for you.
However you can do
std::cout << "hello" << "world" << exclam;
Because std::cout knows how to print a const char[] as well as a string
String literals are simply zero terminated array of chars in C++. There is no operator that allows you to add 2 arrays of chars in C++.
There is however a char array and std::string + operator.
Change to:
const std::string message = std::string("Hello") +", world" + exclam;
In some languages like Python string literals are equivalent to variables of type strings. C++ is not such a language.
In the line where you form your message, the entire expression to the right of the = is performed first, and only then it is assigned to a C++ string. At that point, your "Hello" and your ", World" are still C strings (const char[]) which is why you are getting an error. Addition goes from left to right, so the pair of the C strings is added before you attempt to add the combination to the std::string exclam.
You need to either cast them within the expression (e.g., std::string("Hello")), or create string variables for each like you did with Exclam.
The problem is that basic string literals like "this is a literal" are not of type std::string so they do not concatenate with the + operator.
In post C++14 you can use standard user defined string literals that are of type std::string:
using namespace std::literals; // somewhere in the scope
auto message = "Hello"s + ", world"s; // message is type std::string
See std::literals::string_literals::operator""s.
C-style string literals ("Hello" and ", world") are equivalent to anonymous arrays:
static const char anon1[6] = { 'H', 'e', 'l', 'l', 'o', '\0' };
static const char anon2[8] = { ',', ' ', 'w', 'o', 'r', 'l', 'd', '\0' };
...so when you type "Hello" + ", world", you're trying to add two arrays anon1 + anon2 which is not an operation that C or C++ supports.
Remember, string literals in C/C++ are just arrays (or addresses of arrays). You have to use a string class (e.g. std:string) in order to use operators like +.