Function call parameter, char * vs string default constructor - c++

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.

Related

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.

How does the std::string constructor handle char[] of fixed size?

How does the string constructor handle char[] of a fixed size when the actual sequence of characters in that char[] could be smaller than the maximum size?
char foo[64];//can hold up to 64
char* bar = "0123456789"; //Much less than 64 chars, terminated with '\0'
strcpy(foo,bar); //Copy shorter into longer
std::string banz(foo);//Make a large string
In this example will the size of the banz objects string be based on the original char* length or the char[] that it is copied into?
First you have to remember (or know) that char strings in C++ are really called null-terminated byte strings. That null-terminated bit is a special character ('\0') that tells the end of the string.
The second thing you have to remember (or know) is that arrays naturally decays to pointers to the arrays first element. In the case of foo from your example, when you use foo the compiler really does &foo[0].
Finally, if we look at e.g. this std::string constructor reference you will see that there is an overload (number 5) that accepts a const CharT* (with CharT being a char for normal char strings).
Putting it all together, with
std::string banz(foo);
you pass a pointer to the first character of foo, and the std::string constructor will treat it as a null-terminated byte string. And from finding the null-terminator it knows the length of the string. The actual size of the array is irrelevant and not used.
If you want to set the size of the std::string object, you need to explicitly do it by passing a length argument (variant 4 in the constructor reference):
std::string banz(foo, sizeof foo);
This will ignore the null-terminator and set the length of banz to the size of the array. Note that the null-terminator will still be stored in the string, so passing a pointer (as retrieved by e.g. the c_str function) to a function which expects a null-terminated string, then the string will seem short. Also note that the data after the null-terminator will be uninitialized and have indeterminate contents. You must initialize that data before you use it, otherwise you will have undefined behavior (and in C++ even reading indeterminate data is UB).
As mentioned in a comment from MSalters, the UB from reading uninitialized and indeterminate data also goes for the construction of the banz object using an explicit size. It will typically work and not lead to any problems, but it does break the rules set out in the C++ specification.
Fixing it is easy though:
char foo[64] = { 0 };//can hold up to 64
The above will initialize all of the array to zero. The following strcpy call will not touch the data of the array beyond the terminator, and as such the remainder of the array will be initialized.
The constructor called is one that takes a const char* as an argument. That constructor attempts to copy the character data pointed to by that pointer, until the first NUL terminator is reached. If there is no such NUL terminator then the behaviour of the constructor is undefined.
Your foo type is converted to a char* by pointer decay, then an implicit conversion to const char* occurs at the calling site.
Perhaps there could have been a templatised std::string constructor taking a const char[N] as an argument, which would have allowed the insertion of more than one NUL character (the std::string class after all does support that), but it was not introduced and to do so now would be a breaking change; using
std::string foo{std::begin(foo), std::end(foo)};
will also copy the entire array foo.

Passing a string to a function in C++(object vs string)

What is the difference between the following two in C++?
fun(L"text1")
VS
std::wstring var = "text1"
fun(var)
In the first case, it is being passed as an object while the second case passes it as a wstring.
How should fun() be defined to handle both?
EDIT:
I have two function definitions
fun(void*)
fun(std::wstring)
std::wstring t = "bla";
fun(t);
fun(L"msg");
When fun(t) is called it goes to the definition of fun(std::wstring)
But when fun(L"msg") is called it goes to fun(Void*). Instead I want it to goto fun(std::wstring)
The first is passed as a wide-string literal. In the second case, you pass by value (and hence copy) or by reference an std::wstring object.
To handle both, you have to define two overloads of your function:
void fun(const wchar_t* s);
void fun(const std::wstring& s);
or you can just define the wstring version, because the literal will implicitly convert to a wstring.
To handle both you should define the fun method as:
void fun(const std::wstring& str);
then in both cases you would be passing a const reference to a wstring because the compiler is allowed to implicitly cast one type to another if the type being cast to has a constructor that takes one argument of the type being cast from unless that constructor is marked as explicit.
Example:
class wstring
{
public:
// constructor not marked as explicit and takes one argument of type whar_t*
wstring(const wchar_t* str);
};
wstring myString = L"hello world"; // implicit cast from wchar_t* to wstring
The only difference between the two examples you've given is that in the first you are passing an rvalue (which you can only bind to a const reference) and in the second you are passing an lvalue (which you can bind to both const and non-const reference).
In given examples there is no much difference, because compiler will generate constant data containing your literal and use it in both cases.
In the first case, raw literal will be used from string table of your module. This is as fast as possible code without heap allocation (actually no allocations).
In the second case, compiler will allocate string buffer in the heap, which results into malloc() call and strcpy(). This will increase time of your code and cause more memory fragmentation.
You should use std string classes only when you really need to use their useful methods, otherwise, TCHAR buffers are just excellent choice.

cstring -> c++ string conversion

If I have a function
void x(std::string const& s)
{
...
}
And I am calling it as x("abc"), will string constructor allocate memory and copy data in it?
The std::string constructor will be called with a const char* argument
There is no telling whether memory would be allocated (dynamically), but the chances are that your standard library implementation has the SSO in place, which means it can store small strings without dynamic allocations.
SSO: Meaning of acronym SSO in the context of std::string
The question is tagged with 'performance', so it's actually a good question IMO.
All compilers I know will allocate a copy of the string on the heap. However, some implementation could make the std::string type intrinsic into the compiler and optimize the heap allocation when an r-value std::string is constructed from a string literal.
E.g., this is not the case here, but MSVC is capable of replacing heap allocations with static objects when they are done as part of dynamic initialization of statics, at least in some circumstances.
Yes, the compiler will generate the necessary code to create a std::string and pass it as argument to the x function.
Constructors which take a single argument, unless marked with the explicit keyword, are used to implicitly convert from the argument type to an instance of the object.
In this example, std::string has a constructor which takes a const char* argument, so the compiler uses this to implicitly convert your string literal into a std::string object. The const reference of that newly created object is then passed to your function.
Here's more information: What does the explicit keyword mean in C++?

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.