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)
Related
I like to know pro's and con's for having and not-having such cast. At several places including here on Stack Overflow I can see that the const char* cast is considered bad idea but I am not sure why?
Lack of the (const char*) and forcing to always use c_str() cast creates some problems when writing generic routines and templates.
void CheckStr(const char* s)
{
}
int main()
{
std::string s = "Hello World!";
// all below will not compile with
// Error: No suitable conversion function from "std::string" to "const char *" exists!
//CheckStr(s);
//CheckStr((const char*)s);
// strlen(s);
// the only way that works
CheckStr(s.c_str());
size_t n = strlen(s.c_str());
return 0;
}
For example, if I have a large number of text processing functions that accept const char* as input and I want to be able to use std::string each time I have to use c_str(). But in this way a template function can't be used for both std::string and const char* without additional efforts.
As a problem I can see some operator overloading issues but these are possible to solve.
For example, as [eerorika] pointed, with allowing implicit cast to pointer we are allowing involuntary the string class to be involved in boolean expressions. But we can easily solve this with deleting the bool operator. Even further, the cast operator can be forced to be explicit:
class String
{
public:
String() {}
String(const char* s) { m_str = s; }
const char* str() const { return m_str.c_str(); }
char* str() { return &m_str[0]; }
char operator[](int pos) const { return m_str[pos]; }
char& operator[](int pos) { return m_str[pos]; }
explicit operator const char*() const { return str(); } // cast operator
operator bool() const = delete;
protected:
std::string m_str;
};
int main()
{
String s = "Hello";
string s2 = "Hello";
if(s) // will not compile: it is a deleted function
{
cout << "Bool is allowed " << endl;
}
CheckStr((const char*)s);
return 0;
}
I like to know pro's and con's for having and not-having such cast.
Con: Implicit conversions often have behaviour that is surprising to the programmer.
For example, what would you expect from following program?
std::string some_string = "";
if (some_string)
std::cout << "true";
else
std::cout << "false";
Should the program be ill-formed because std::string is has no conversion to bool? Should the result depend on the content of the string? Would most programmers have the same expectation?
With the current std::string, the above would be ill-formed because there is no such conversion. This is good. Whatever the programmer expected, they'll find out their misunderstanding when they attempt to compile.
If std::string had a conversion to a pointer, then there would also be a conversion sequence to bool through the conversion to pointer. The above program would be well-formed. And the program would print true regardless of the content of the string, since c_str is never null. What if programmer instead expected that empty string would be false? What if they never intended either behaviour, but used a string there by accident?
What about the following program?
std::string some_string = "";
std::cout << some_string + 42;
Would you expect the program to be ill-formed because there is no such operator for string and int?
If there was implicit conversion to char*, the above would have undefined behaviour because it does pointer arithmetic and accesses the string buffer outside of its bounds.
// all below will not compile with
strlen(s);
This is actually a good thing. Most of the time, you don't want to call strlen(s). Usually, you should use s.size() because it is asymptotically faster. The need for strlen(s.c_str()) is so rare, that the little bit of verbosity is insignificant.
Forcing the use of .c_str() is great because it shows the reader of the program that it is not a std::string that is passed to the function / operator, but a char*. With implicit conversion, it is not possible to distinguish one from the other.
... creates some problems when writing generic routines and templates.
Such problems are not insurmountable.
If by "having a cast" you mean a user defined conversion operator, then the reason it does not have it is: to prevent you from using it implicitly, possibly inadvertently.
Historically, unpleasant consequences of an inadvertent use of such conversion stem the fact that in the original std::string (per C++98 specification) the operation was heavy and dangerous.
The original std::string was not trivially convertible to const char *, since the string object was not originally intended/required to store a null-terminator character. Under those circumstances, conversion to const char * was a potentially heavy operation that generally allocated an independent buffer and copied the entire controlled sequence to that buffer.
The independent buffer mentioned above (if used) had potentially "unexpected" lifetime. Any modifying operation on the original std::string object triggered invalidation/deallocation of that buffer, rendering previously returned pointers invalid.
It is never a good idea to implement such heavy and dangerous operations as implicitly-invokable conversion operators.
The original C++ standard (C++98) did not have such feature as explicit conversion operators. (They first appeared in C++11.) A dedicated named member function was the only way to somehow make the conversion explicit in C++98.
Today, in modern C++, we can define a conversion operator and still prevent it from being used implicitly (by using explicit keyword). One can argue that under such circumstances implementing the conversion by an operator is a reasonable approach. But I'd still argue that it is not a good idea. Even though the modern std::string is required to store its null-terminator (i.e. c_str() no longer produces an independent buffer), the pointer returned by the conversion to const char * is still "dangerous": many modification operations applied to std::string object may (and will) invalidate this pointer. To emphasize the fact that this is not a mere safe and innocent conversion, but rather an operation that produces a potentially dangerous pointer, it is quite reasonable to implement it by a named function.
What is the difference between these two string initializations in c++?
i get the same output in both the programs.
Program 1
void main(){
string a = "hello";
cout<<a;
}
program 2
void main(){
string a = (char *)"hello";
cout<<a;
}
"hello" is a string literal. Its type is
const char[N], where N is the size of the string [...] including the null terminator.
So in this case, the type is const char[6]. Note the const.
Now std::string can be constructed (constructor 5 in the link) from a const char*. Again, note the const.
In C++, you can pass a non-const object into a function that expects a const. In your case, your cast (char *) removes the const, but then immediately puts the const back on in the constructor call.
So basically, no difference. They will compile to exactly the same thing.
A few additional notes:
casting away const-ness is very dangerous. If you had actually tried to change anything in the char array, your program would have had Undefined Behaviour.
using namespace std; is widely considered to be bad practice.
void main() is not a valid signature for the main function; it must return an int.
Using C-style casts in C++ can also be considered a bad practice - it is harder to spot in code, and C++ provides safer equivalents for more specific situations: const_cast, static_cast, dynamic_cast, and (the most dangerous) reinterpret_cast.
Nothing actually, that additional char * type casting is not needed.
In both the cases copy initialization is done.
string (const char* s);
read more on copy initialization
std::string is a typedef for a specialization of basic_string class templated on a char (objects that represent sequences of characters).
The expression string a = "hello" corresponds to a stream of characters whose size is allocated statically.
Short, simple and typesafe std::cout is a typedef of ostream class templated on standard objects (provides support for high-level output operations).
cout means "the standard character output device", and the verb << means "output the object".
cout << a; sends the string like a stream to the stdout.
char * are special pointers to a constant character, they point to ASCII strings e.g.:
const char * s = "hello world";
The expression (char *)"hello" corresponds to a char * pointer, where you are casting away const, but then the constructor immediately puts the const back on the call.
cout, therefore, will print a string because it has a special operator for char * wich it will treat as a pointer to (the first character of) a C-style string that outputs strings.
char* or const char*, cout will treat the operand as a pointer to (the first character of) a C-style string, and prints the contents of that string:
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) {};
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")
I want to create a move constructor that takes string literal, and then move that c string to a member pointer.
The best solution I could write is giving a warning:
deprecated conversion from string constant to 'char*' [-Wwrite-strings]
CTextBlock cctb("move(H)");
^
the code:
#include <iostream>
using namespace std;
class CTextBlock
{
public:
CTextBlock(char* &&text)//move constructor
{
pText = text;
}
private:
char *pText;
};
int main()
{
CTextBlock cctb("move(H)"); //WARNING
return 0;
}
First off, the type of string literals is char const[N] (for a suitable constant N). This array can be assigned to a char const* in which case it will decay into a pointer to the first element. It cannot be converted to a char*. Prior to C++11 the conversion to char* was allowed to deal with existing code which wasn't const-correct (e.g., because it started as C code before C got const). This conversion was removed for C++11.
Question is what you actually try to achieve, though: string literals are immutable and persist for the entire life-time of the program. You can just keep as many pointers to them as you want and there is no point in moving pointers as these are entirely cheap to copy.
In your question you indicate that you want to create a move constructor but move constructors take an rvalue reference of the class they are for, e.g., this would be a move constructor for you class:
CTextBlock::CTextBlock(CTextBlock&& other)
: pText(other.pText) {
other.pText = 0;
}
(your class doesn't show any ownership semantics for the pointer pText in which case move construction doesn't really make much sense; the above code assumes that there is some ownership semantics and that a null pointer indicates that the object doesn't own anything).
Just because an argument is constrained to be an rvalue reference doesn't mean that function is a move constructor. All it implies is that the argument is an rvalue an it can reasonably be assume that it's current representation doesn't need to be retained. The string literal appears to be an rvalue because the the string literal is converted into a [temporary] pointer to the start of the array.
A constructor that gets reasonably close to allowing only literals can be realized as a template:
#include <cstddef>
#include <assert>
struct X
{
char const * str;
std::size_t len;
template <std::size_t N>
X(char const (&a)[N]) : str(a), len(N - 1)
{
assert(a[len] == '\0'); // true for string literals
}
};
It's not fool-proof, though, since it will also bind to named character arrays, and the length computation is dubious if your string also contains null values. But if you're trying to avoid accidental use of dynamic values (e.g. in an algorithm that builds strings from expressions and literal strings), this is fairly useful.
A string literal is a const char *. This is true whether or not it's used as an lvalue or an rvalue.
If you review your code again, you are, therefore, attempting to store a const char * into a char *, and that's where your compiler diagnostic is coming from. Your move constructor is taking an rvalue reference to a char *, and not a const char *. Change it to a const char *, and change the pText class member to a const char *, and what you're trying to do should work.