I get an error when trying to print concatenated strings:
std::cout << "some string" + "another string";
I get this error:
Operator does not take these operands.
I thought "some string" was a std::string literal. What is going on?
Binary + is not supposed to take these operands. C++ language does not have such + operator and never had one. You cannot add two string literals to each other.
Why you mention std::string in the question title is not clear to me. There's no std::string in your example. String literals are not std::string objects. String literals are just arrays of const char. They have no relation to std::string and they will not be magically converted to std::string for you.
If you want to use std::string in this case you have to convert at least one of your literals to std:string explicitly
cout << std::string("some string") + "another string";
In the case the overload resolution rules will make the compiler to consider the binary + operator for std::string objects and convert the second operand to std::string implicitly.
Those are not std::string-operands, where + really concatenates, but string literals.
String literals represent arrays of constant characters (including an implicit 0-terminator), which are not addable (const char[]). Neither are the pointers they decay to.
Still, concatenating them is really easy: Just leave out anything between them but whitespace, and the compiler will do it for you.
As an aside, since C++14 one can actually write std::string-literals:
#include <string>
using namespace std::literals::string_literals;
// the last two are inline-namespace, could leave them off to get more.
...
"std::string-literal"s // Note the `s` behind the string-literal.
To create a std::string literal, you have to do the following:
#include <string>
#include <iostream>
int main() {
using namespace std::string_literals;
std::cout << "some string"s + "another string"s;
}
notice the trailing s.
The sequence "some string" is not a std::string literal, but rather a const char[12] buffer of raw characters. This comes from C, where there was no std::string. In addition, it means that if you prefer a different string library, std::string has no built-in advantage.
With you post-fix with that s (after bringing the literals into view), you get a std::string literal.
This is a C++14 feature. In C++03 you can get a similar effect by doing
#include <string>
#include <iostream>
int main() {
std::cout << std::string("some string") + std::string("another string");
}
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.
Why does the code below print "NO"?
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
const std::string abc = "hello";
if (abc.c_str() == "hello")
{
cout << "\nYES";
}
else
{
cout << "NO";
}
}
The .c_str() returns const char* whereas "hello" should be interpreted as either const char* or std::string, both are valid. But why doesn't it print "YES". strcmp() does print "YES", when used instead. But my question is on the code above, is this a compiler bug?
"hello" should be interpreted as either const char* or std::string both are valid.
No. "hello" is never interpreted as std::string. It has the type const char[6], which in this case is converted to const char*. This conversion for arrays is called decaying.
But why doesn't it print YES.
When you compare two pointers, you compare whether they point to the same object. The pointers that you use compare unequal because the string literal, and the buffer of the std::string are not the same object.
is this a compiler bug?
No. It is a bug in your code.
so what would you suggest as the right approach with using c_str() and std::string?
The correct way is to compare the content of null terminated character arrays is std::strcmp.
Alternatively, you could use the comparison operator with the std::string directly, without using the pointer returned by c_str. The comparison operator of std::string compares with the content of a null terminated string.
It's not a compiler bug. abc.c_str() is giving you the value of a pointer to the std::string data buffer and since C++11, it's impossible for that to be the same address as the address of the literal "hello" as that standard forbids copy-on-write semantics for std::string. (I believe this was theoretically possible in C++03 and earlier, out of interest.)
If you want to compare a literal with a std::string, use
if (abc == "hello")
as std::string has an appropriate overload for the == operator.
abc.c_str() is a pointer to the internal buffer of the abc object, you're then doing a pointer comparison with the string literal "hello" which returns false.
You are comparing the wrong way, try:
Instead of:
if (abc.c_str() == "hello")
use:
if (!strcmp(abc.c_str(), "hello"))
I referred to http://en.cppreference.com/w/cpp/language/typeid to write code which does different things for different types.
The code is as below and the explanation is given in the comments.
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T>
void test_template(const T &t)
{
if (typeid(t) == typeid(double))
cout <<"double\n";
if (typeid(t) == typeid(string))
cout <<"string\n";
if (typeid(t) == typeid(int))
cout <<"int\n";
}
int main()
{
auto a = -1;
string str = "ok";
test_template(a); // Prints int
test_template("Helloworld"); // Does not print string
test_template(str); // Prints string
test_template(10.00); // Prints double
return 0;
}
Why does test_template(str) print "string" whereas test_template("Helloworld") does not?
BTW, my g++ version is g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609.
In this call
test_template("Helloworld"); // Does not print string
the argument "Helloworld" is a string literal that has type const char[11].
Because the function parameter is a referenced type
void test_template(const T &t)
^^^
then within the function the argument (more precisely the parameter) has the type const char ( &t )[11].
String literals in C++ have types of constant character arrays with the number of elements equal to the number of characters in string literal plus the terminating zero.
In this call
test_template(str);
the argument has type std::string because the variable str is declared like
string str = "ok";
^^^^^^
It was initialized by the string literal "ok" nevertheless the object itself is of the type std::string.
String literals in C++ are of type const char[N+1], where N is the number of characters in the string. std::string is a standard library class which owns a string and provides a number of operations over it. A std::string can be constructed from a const char[N], but they are not the same thing.
String literals like "Helloworld" are constants arrays of characters.
The std::string class have a constructor that can take pointers to string literals, but a string literal is in itself not a std::string object.
As a side-note, using a function like your is considered a code-smell and bad design. Use overloaded functions taking different arguments instead. That will also solve your problem with the strings.
I made a template which adds the data it is given. If I use it like this, the compiler declares in_1 and in_2 as const char *, and the code doesn't compile.
#include <iostream>
using namespace std;
template <class T>
T addstuff(T part_1, T part_2){
return(part_1+part_2);
}
int main(int argc, char const *argv[])
{
auto in_1="Shut ";
auto in_2="up.";
cout<<addstuff(in_1, in_2)<<endl;
return 0;
}
If I declare in_1 and in_2 std::string, it works like a charm.
Why can't (or doesn't) the compiler declare those strings automatically std::string?
The reason you can't "write" to your auto variable is that it's a const char * or const char [1], because that is the type of any string constant.
The point of auto is to resolve to the simplest possible type which "works" for the type of the assignment. The compiler does not "look forward to see what you are doing with the variable", so it doesn't understand that later on you will want to write into this variable, and use it to store a string, so std::string would make more sense.
You code could be made to work in many different ways, here's one that makes some sense:
std::string default_name = "";
auto name = default_name;
cin >> name;
If you use string literals, auto will work as expected.
In C++14, C++17 or C++20, you can place an s after the quotes, and it will create a std::string instead of a const char* string.
This can be used together with auto to create a std::string:
auto hello = "hello"s;
String literals are not enabled by default. One way of enabling string literals is to place the following at the top of the source file:
#include <string>
using namespace std::string_literals;
As an example, this loop works for std::string (with s added to the string literal), but not for const char* type string literals:
for (auto &x : hello) {
std::cout << "letter: " << x << std::endl;
}
Here is the cppreference page for the ""s operator.
Because string literals have type const char[N+1], not std::string.
This is just a fact of the language.
They could have made it so that auto has a special case for string literals, but that would be inconsistent, surprising and of very little benefit.
auto will declare the variable as the compile-time type of the expression you initialize it to.
String literals are of type const char*, not std::string.
I need a way to get this PHP behaviour in C++:
$foo = "PHP";
$bar = "this is a " . $foo . " example.";
Is there something close to that, or do I have to do lots of strcat?
Easy enough with std::string:
std::string foo = "C++";
auto bar = "this is a " + foo + " example.";
Just make sure one of the first two operands is a std::string, not both const char * or something.
As noted below, this result is being used in CreateProcess as a char * (LPSTR) argument. If the argument was const char *, c_str() would be perfectly acceptable to pass in. However, it is not, which means you should assume it modifies the string. MSDN says this:
The Unicode version of this function, CreateProcessW, can modify the contents of this string.
Since this is char *, it's evidently using CreateProcessA, so I'd say a const_cast<char *> should work, but it's better to be safe.
You have two main options, one for C++11 and later, and one for pre-C++11.
C++11
std::string's internal buffer is now guaranteed to be contiguous. It's also guaranteed to be null-terminated. That means you can pass a pointer to the first element:
CreateProcess(..., &str[0], ...);
Make sure the function only overwrites indices within [0, size()) in the internal array. Overwriting the guaranteed null-terminator is not good.
C++03
std::string is not guaranteed to be contiguous or null-terminated. I find it best to make a temporary std::vector, which guarantees the contiguous part, and pass a pointer to its buffer:
std::vector<char> strTemp(str.begin(), str.end());
strTemp.push_back('\0');
CreateProcess(..., &strTemp[0], ...);
Also note MSDN again:
The system adds a terminating null character to the command-line string to separate the file name from the arguments. This divides the original string into two strings for internal processing.
That seems to suggest that the null-terminator here isn't necessary, but there's no size parameter, so I'm not completely sure.
Yes, you can use std::string:
std::string foo = "PHP";
std::string bar = std::string("This is a") + foo + std::string(" example.")
In C++, you can use std::string:
std::string foo = "C++"
std::string bar = std::string("this is a") + foo + " example.";
You need the std::string(...) to make the first string into a std::string, since otherwise it's a const char *, which doesn't have operator+ to join it with string.
There are probably at least 5 other possible ways to do this, like almost always in C++.
[Again being too slow in my typing]
C++ provides the string class.
string foo = "PHP";
string bar = string("this is a ") + foo + string(" example.");
You can use std::string for this. So try this:
#include <string>
int main() {
std::string foo = "C++";
std::string bar = "this is a " + foo + " example.";
return 0;
}
If you are using C++ with the standard C++ library, you can use a std::stringstream to accomplish that. The code would look something like this:
#include <sstream>
std::string const foo("C++");
std::stringstream bar;
bar << "this is a " << foo << " example";
std::string const result(bar.str());
If for some reason you cannot use the C++ standard library you are unfortunately stuck with the likes of strcat.