In C++, if string is a class, why do we not need the dot operator or an object to store data in a string?
Classic string:
string str = "ABC";
Why can we directly pass ABC using " " instead of doing it like
string str;
str.data = "ABC";
But we need to use objects to access the functions.
Example:
str.length();
Why do we do this?
Is string some special kind of class?
string str = "ABC"; is not assignment. It is construction. Specifically it calls the std::string constructor taking a const char * argument.
It's the same as doing
string str("ABC");
just different syntax.
Assignment also works. You do this:
string str;
str = "ABC";
See also:
Copy initialization
std::string constructors
std::basic_string::operator=
std::basic_string has a constructor like this:
basic_string( const CharT* s, const Allocator& alloc = Allocator() );
Constructs the string with the contents initialized with a copy of the null-terminated character string pointed to by s.
But the important point to note is that this constructor is not explicit, thus the compiler can do implicit conversion of null terminated character string during constructor call.
For example, following code compiles without any issue:
class Foo {
public:
Foo(int) {}
};
int main() {
Foo f = 10;
}
It won't compile if the constructor is written as:
explicit Foo(int) {}
In C++ a string literal is not a std::string, but a C style character array(char[N]). And yes std::string or any other 3rd party string type that you may see is a class with a converting constructor accepting character arrays as input. More precisely, std::string is a type alias for an instansiation of the template std::basic_string. In short words, before you can do anything with a string literal, you'd better convert it to a string:
std::string{"ABC"}.size()
Or you will have to switch to C API which is not recommended for beginners:
strlen( "ABC")
Related
I was reading Chapter 9 of the 2nd edition of A Tour of C++ by Bjarne Stroustrup and was puzzled by the use of {&king[0],2} where king is a string variable. I get that it returns the first 2 strings but I don't know what the name is for this to look up more details about it like:
Does the & ampersand indicate that it is a reference or does it switch it to pointer?
In what version was this feature introduced? I know the book is based on C++17 but don't know what version began supporting it.
// Strings and Regular Expressions
#pragma once
#include <string>
using namespace std;
string cat(string_view sv1, string_view sv2) {
string res(sv1.length()+sv2.length(), '\0');
char* p = &res[0];
for (char c : sv1) // one way
*p++ = c;
copy(sv2.begin(), sv2.end(), p); // another way
return res;
}
void tryCat() {
string king = "Harold";
auto s1 = cat(king, "William"); // string and const char*
auto s2 = cat(king, king); // string and string
auto s3 = cat("Edward", "Stephen"sv); // const char* and string_view
auto s4 = cat("Canute"sv, king);
auto s5 = cat({&king[0],2}, "Henry"sv); // HaHenry
auto s6 = cat({&king[0],2}, {&king[2],4}); // Harold
cout << s6 << endl;
}
int main () {
tryCat();
}
The funcction
string cat(string_view sv1, string_view sv2) {
accepts as its arguments two objects of the type std::string_view.
Such a construction
{&king[0],2}
is used to construct an object of the type std::string_view using the constructor
constexpr basic_string_view(const charT* str, size_type len);
The expression &king[0] in the above construction has the pointer type char * and yields the address of the first character of a string.
As the variable king is declared like
string king = "Harold";
then in fact the object of the type std::string_view is built from first two characters of the string like "Ha".
I think it's called copy-list-initialization, specifically form #7 at List initialization - Cppreference. Copy-list-initialization was introduced in C++11.
function( { arg1, arg2, ... } ) (7)
&king[0] gets the underlying char* array from the std::string. &king[2] gets the address of the third character of that array. In this case, & is the address-of operator, not a reference. [0] is a subscript. The second item in the braces after the comma is the second argument to the string_view constructor which specifies the length of the string.
Here's the description of copy-list-initialization from the cppreference link.
copy-list-initialization (both explicit and non-explicit constructors are considered, but only non-explicit constructors may be called)
initialization of a named variable with a braced-init-list after an equals sign
in a function call expression, with braced-init-list used as an argument and list-initialization initializes the function parameter
So I have this little snippet where it thinks "abc" isn't a string but rather a const char [4], and so I can't assign it to my object. I've searched but haven't found any working solutions. Thanks in advance.
Tekst t = "abc";
Tekst Tekst::operator=(std::string& _text){
return Tekst(_text);
}
Edit: since this is a major staple of almost every exercise in my Object Oriented Programming class, due to whatever reasons, we can't change anything that's in int main(), so changing Tekst t = "abc"; is a no-go.
Edit 2:Tekst(std::string _text) :text(_text) {};
Compiler doesn't think "abc" is a const char [4]. It is const char [4] and you think that it should be std::string, which is not correct. std::string can be implicitly constructed from const char *, but they are nowhere near the same.
You problem is actually that you're trying to bind a temporary to a non-const reference, which is impossible in C++. You should change the definition of your operator to
Tekst Tekst::operator=(const std::string& _text){
// ^ const here
return Tekst(_text);
}
This will make your operator technically valid (as in, it compiles and there is no Undefined Behaviour). However, it does something very non intuitive. Consider the following:
Tekst t;
t = "abc";
In this example, t will not have any "abc" inside. The newly returned object is discarded and t is unchanged.
Most likely, your operator should look like this:
Tekst& Tekst::operator=(const std::string& _text){
this->text = _text; //or however you want to change your object
return *this;
}
Refer to the basic rules and idioms for operator overloading for more information about what is and what isn't expected in each operator.
On a semi-related note, you can have std::string from literal in C++14 and up:
#include <string>
using namespace std::string_literals;
int main() {
auto myString = "abc"s;
//myString is of type std::string, not const char [4]
}
However, this wouldn't help with your case, because the main problem was binding a temporary to non-const reference.
Thanks to #Ted Lyngmo, "This should help: godbolt.org/z/j_RTHu", turns out all i had to do was add a separate constructor for taking in const char*
Tekst(const char* cstr) : Tekst(std::string(cstr)) {}
Tekst t = "abc"; is just syntax sugar for Tekst t("abc"); which means it does not even consider your operator= at all, it uses the class's constructor instead.
In order for Tekst t = "abc"; to compile, you need a constructor that accepts either a const char* or a const std::string& as input. However, the constructor you showed takes a std::string by value instead of by const reference, so it can't be used for string literals. So you need to add a new constructor for that:
Tekst(const char *_text) :text(_text) {};
string s = "abc";
The above statement will first invoke string ( const char * s ) constructor, then invoke copy constructor according to What are the differences in string initialization in C++? .
Here is the question: how C++ know it should invoke string ( const char * s ) to convert literal string "abc" to a temporary string object?
Note:
copy constructor won't be invoked in copy initialization.
Initializing an object by using the syntax
string s = "abc";
is called copy initialization.
There are several scenarios where that is legal initialization. In all cases the RHS must be convertible to a string for it to work.
One way a string literal can be converted to a string is through the constructor of string that takes a char const*. That is called a user defined conversion.
Summary of all answers:
Reference:
http://en.cppreference.com/w/cpp/language/implicit_cast
http://en.cppreference.com/w/cpp/language/copy_initialization
http://en.cppreference.com/w/cpp/language/converting_constructor
This cast puzzles me:
#include <string>
#include <iostream>
#include <memory>
using namespace std;
int main() {
string str1 = (string)"I cast this thing" + " -- then add this";
cout << str1 << endl;
}
Can someone explain why this c-style cast to string works (or is allowed)? I compared the generated optimized assembly with that from:
string str1 = string("I construct this thing") + " -- then add this";
and they appear to be identical, so I feel like I'm forgetting some c++ semantics that actually allow this kind of cast/construction to be interchanged.
std::string str2 = std::string("I construct this thing") + " -- then add this";
A C-style cast will do a const cast and static cast or reinterpret cast whichever is possible.
A static cast will use a user-defined conversion if defined.
std::string has a constructor string(const char *).
The syntaxes std::string("something"), static_cast<std::string>("something") and (std::string)"something" are equivalent. They will all construct a temporary std::string using the std::string::string(const char *) constructor. The only difference between the syntaxes would be when casting pointers.
Yep, if you have a constructor that takes in a single argument like that it will be used to cast the argument type to the object type. This is why we can pass const char* to functions taking strings.
Converting constructor
std::string has a constructor in the form of
basic_string( const CharT* s,
const Allocator& alloc = Allocator() );
This acts as a conversion operator. If you cast a string literal to a std::string it will call this constructor and create a temporary std::string
I've got a couple questions that I think will be quite easy for someone with C++ experience to answer, I'll bold the quesitons for the TL;DR
Given the following code:
void stringTest(const std::string &s)
{
std::cout << s << std::endl;
}
int main()
{
stringTest("HelloWorld");
}
Hopefuly someone can point out the error in my thought process here:
Why does the parameter in stringTest have to be marked const when passed a C-Style string? Isn't there an implicit conversion to an std::string that takes place using its cstyle string constructor, therefore "s" is no longer a reference to a literal (and is not required to be const).
Furthermore, what would a cstyle string constructor look like, and how does the compiler know to invoke this upon seeing:
stringTest("HelloWorld");
Does it simply recognize a string literal to be something like a char*?
I've stumbled upon these questions while studying copy constructors. Another quick quesiton for my own clarification...
In the case of something like:
std::string s = "HelloWorld";
Is the cstyle string constructor used to instantiate a temporary std::string, and then the temporary string is copied into "s" using the string copy constructor?:
std::string(const std::string&);
Why does the parameter in stringTest have to be marked const when passed a C-Style string?
It only has to when the parameter is a reference, since a temporary std::string is constructed from the char const* you pass in and a non-const reference to a temporary is illegal.
Does it simply recognize a string literal to be something like a char*?
A string literal is a char const array, which decays to char const*. From that, the compiler infers that it should use the non-explicit constructor std::string::string(char const *) to construct the temporary.
Is the cstyle constructor used to instantiate a temporary std::string, and then the temporary string is copied into "s" using the string copy constructor?
It's a bit more complicated than that. Yes, a temporary is created. But the copy constructor may or may not be called; the compiler is allowed to skip the copy construction as an optimization. The copy constructor must still be provided, though, so the following won't compile:
class String {
String(char const *) {}
private:
String(String const &);
};
int main()
{
String s = "";
}
Also, in C++11 the move constructor will be used, if provided; in that case, the copy constructor is not required.
Does it simply recognize a string literal to be something like a
char*?
This part of the original question wasn't answered as clearly as I'd have liked. I fully endorse (and up-voted) Yossarian's answer for the rest though.
Basically, you need to understand what the compiler is doing when it sees a string literal in the code. That array of chars (as any c-style string really is) is actually stored in a completely different location than the code it's a part of (depending on the architecture, numeric literals can be stored at the location itself as part of the assembly/binary instruction). The two blocks of code here are "more or less" equivalent (ignore lack of includes or namespace declarations) :
int main(void)
{
cout << "Hello!" << endl;
return 0;
}
This is closer to what's "really" happening:
const char HELLO_STR[] = { 'H', 'e', 'l', 'l', 'o', '!', 0 };
int main(void)
{
cout << HELLO_STR << endl;
return 0;
}
Forgive me if I made an error in array init or whatever, but I think this expresses what I mean as for where the string literal is "really" stored. It's not in-line, but is an invisible constant to another part of the program where it's defined. In addition, some (most?) compilers out there also arrange the string literals "together" so that if you have the same literal used in 50 places, it only stores one of them, and all of them refer back to the same constant, saving memory.
So remember that any time you're using a string literal, you're using a const char[N] that exists "invisibly" somewhere, that is implicitly converted to const char*.
Why does the parameter in stringTest have to be marked const when passed a C-Style string?
EDIT:
Temporaries must be immutable. See larsmans comment and answer, he is right.
Simple reason:
void change(std::string& c) { c = "abc"; }
change("test"); // what should the code exactly do??
Furthermore, what would a cstyle string constructor look like, and how does the compiler know to invoke this upon seeing:
It looks up std::string for string(char*) constructor
In the case of something like:
std::string s = "HelloWorld";
Is the cstyle constructor used to instantiate a temporary std::string, and then the temporary string is copied into "s" using the string copy constructor?:
std::string(const std::string&);
No. In this exact case (TYPE variable = SOMETHING), it is the same as writing TYPE variable(SOMETHING);. So, no copying is used.
The const string & s is in this example required to invoke the constructor from the argument "HelloWorld". The constructor used is the type-conversion construction.
A string& s won't do because s is directly referencing a string object.
The type conversion is defined by something similar to
basic_string(const _CharT* __s);
With a typedef
typedef basic_string<char> string;
So the declaration would evaluate to
basic_string(const char * __s)