Which std::string constructor is being called here? - c++

I am trying to construct a string in C++ as below.
const char *cstring = "abcd";
const string cppstr = string(cstring, cstring + strlen(cstring));
This is working fine and cppstr has the value "abcd" even though it doesn't match with any of the string constructors specified in the standard. Can any one please let me know which constructor of string is invoked in this particular case.

There is a templated constructor that takes as its input two InputIterators. (See the cppreference.org reference, constructor (6)). Raw C++ pointers meet all the requirements of InputIterators (in fact, they're RandomAccessIterators). Therefore, calling
string(cstring, cstring + strlen(cstring)
invokes this constructor. That constructor works by iterating across the range of the elements delineated by the iterator and constructing a string as a copy of those elements.
As a note, you can also just write
const string cppstr{cstring, cstring + strlen(cstring)};
here instead of assigning cppstr a value.

Related

Function call parameter, char * vs string default constructor

While calling a function/method in C++11 and above, which one is better (if any difference)?
Lets assume this function/method:
void func(std::string s) { ... }
Which one is best between the following?
func(std::string())
or
func("")
And more generally, is there any advantage to always call the constructor explicitly during initialization or parameter passing?
It's better to call the default constructor, because it's guaranteed to not do any unnecessary work.
When passing an empty string literal, it could be that the string implementation does some work processing that string (compute its length for example). An empty string literal isn't a magic bullet that can be treated differently from non-empty string literals. It's type is const char[1], which decays into const char*, and that's it - the std::string constructor dealing with this literal will end up doing more work than necessary.
From cppreference for std::string::string():
Default constructor. Constructs empty string (zero size and unspecified capacity). If no allocator is supplied, allocator is obtained from a default-constructed instance.
... and for std::string::string(const char*):
Constructs the string with the contents initialized with a copy of the null-terminated character string pointed to by s. The length of the string is determined by the first null character. [...]
For further reading, see also this short article.
I would like to compare func(std::string()) with func(""):
func(std::string())
You create an std::string object with default parameter is empty string
Then pass std::string object to func function. You pass it by value, and a new std::string object will be allocated in stack memory, and call a copy constructor to initialized it.
In this case, there are two std::string object is allocated.
func("")
You pass an empty string, so compiler will allocate a std::string object in stack memory, and use std::string(const char*) constructor.
In this case, there is only 1 std::string object allocated.
So, I think for this specific case, func("") maybe better.

What is the difference between the following declarations?

string str("Hello World");
string str="Hello World";
I don't seem to understand the difference between the two. According to my textbook, the operation that the first statement performs is "Initialization constructor using C string". So does the first statement define a C string and the second statement define a C++ string? Also please explain the difference between a C string and a C++ string.
Both lines create a C++ std::string named str. And both initialize them from a C string. The difference is, how they are initialized:
The first is direct initialization:
string str("Hello World");
This calls the string(const char *) constructor.
The second is copy initialization:
string str = "Hello World";
This needs that the string(const char *) constructor is non-explicit (for the previous method, the constructor can be explicit).
We have a little bit differing behavior, depending on the version of the standard:
pre C++17: first, a temporary object is created (with string(const char *)), and then the copy (or move) constructor is called to initialize str. So, the copy (or move) constructor needs to be available. The copy constructor phase can be elided (so the object will be created just like as the direct initialization case), but still, the copy (or move) constructor needs to be available. If it is not available, the code will not compile.
post C++17: here, because the standard guarantees copy elision, only the string(const char *) constructor is called. Copy (or move) constructor doesn't need to be available. If copy constructor is not available, the code still compiles.
So, for this particular case, there is no real difference in the end between the two initializations, and therefore, both str strings will become the same.
Both lines define a variable of type std::string named str that is constructed by the constructor of std::string that takes a char const* as its argument. There is no difference in those lines.
[...] C string [...] C++ string [...]?
What is commonly called a C-string is nothing but a zero terminated array of char:
"foobar"; // an array of 7 const chars. 7 instead of 6 because it is 0-terminated.
char foo[] = "foobar"; // an array of 7 chars initialized by the string given
std::string however is a class of the C++ standard library that manages string resources of dynamic length.
"Hello World" is the c-string (null terminated sequence of characters). string (or std::string as its complete name is) is a c++ string (not null terminated) in both cases.
Both lines call the same constructor that takes the c string and constructs a std::string.

What's the difference between "" and {} for initializing an empty string?

string a = "";
string b = {};
I couldn't really find a good reference explaining the difference between them. Does a compiler see them differently? Then, why?
a is constructed using copy initialisation.
b is constructed using copy list initialisation.
For a std::string the compiler will produce the same thing; a zero length string.
But the mechanism by which the string is constructed may well be different - a compiler, conceptually at least, will have to traverse the anonymous temporary const char[] passed to construct a.
For other types there may be differences; research the two terms above for more details.
In this case, no difference.
string b = {};
initializes the string with the type's default value, which is an empty string.
string a = "";
initializes the string with a specific value, which happens to also be an empty string.
Note that just doing string c; would also create an empty string.

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++ string declaration

I am learning C++ from the beginning and I don't get the whole strings topic.
What is the difference between the following three codes?
std::string s = std::string("foo");
std::string s = new std::string("foo");
std::string s = "foo";
std::string s = std::string("foo");
This creates a temporary std::string object containing "foo", then assigns it to s. (Note that compilers may elide the temporary. The temporary elison in this case is explicitly allowed by the C++ standard.)
std::string s = new std::string("foo");
This is a compiler error. The expression new std::string("foo") creates an std::string on the free store and returns a pointer to an std::string. It then attempts to assign the returned pointer of type std::string* to s of type std::string. The design of the std::string class prevents that from happening, so the compile fails.
C++ is not Java. This is not how objects are typically created, because if you forget to delete the returned std::string object you will leak memory. One of the main benefits of using std::string is that it manages the underlying string buffer for you automatically, so new-ing it kind of defeats that purpose.
std::string s = "foo";
This is essentially the same as #1. It technically initializes a new temporary string which will contain "foo", then assigns it to s. Again, compilers will typically elide the temporary (and in fact pretty much all non-stupid compilers nowadays do in fact eliminate the temporary), so in practice it simply constructs a new object called s in place.
Specifically it invokes a converting constructor in std::string that accepts a const char* argument. In the above code, the converting constructor is required to be non-explicit, otherwise it's a compiler error. The converting constructor is in fact non-explicit for std::strings, so the above does compile.
This is how std::strings are typically initialized. When s goes out of scope, the s object will be destroyed along with the underlying string buffer. Note that the following has the same effect (and is another typical way std::strings are initialized), in the sense that it also produces an object called s containing "foo".
std::string s("foo");
However, there's a subtle difference between std::string s = "foo"; and std::string s("foo");, one of them being that the converting constructor can be either explicit or non-explicit in the above case.
std::string s = std::string("foo");
This is called copy initialization. It is functionally the same as direct initialization
std::string s( "foo" );
but the former does require that the copy constructor is available and compilers may create a temporary object but most will elide the temporary and directly construct s to contain "foo".
std::string s = new std::string("foo");
This will not compile because new returns a pointer. To make it work you'd need the type of s to be a std::string *. Then the line dynamically allocates an std::string object and stores the pointer in s. You'll need to delete it once you're done using it.
std::string s = "foo";
This is almost the same as first. It is copy initialization but it has an added constraint. It requires that the std::string class contains a non-explicit constructor that takes a const char *. This allows the compiler to implicitly construct a temporary std::string object. After that the semantics are identical to case 1.
Creates a temporary string object and copies the value to s
Does not compile, new std::string("foo") returns a pointer to some newly allocated memory.
For this to work, you should declare s as a pointer to a string std::string* s.
Constructs a string from a C-string.
You should use the third option in most - if not all - cases.
1 will create a temporary variable (right hand side), then call the assignment operator to assign the value to s
2 will create an instance of std::string on the heap and return a pointer to it, and will fail in the assignment because you can't assign a pointer to a non-pointer type
3 will build a std::string and initialize it from a const char*
On the number 1, you are creating a temporary string using the constructor and then assigning it to s.
Number 2 doesn't even compile.
On number 3, you are creating a new string and then assign a value to it.