const char cp[]="jkasdkasjsad";
string a=static_cast<string>(cp);//"const string a" also runs without any error
I have stuck at the above code for the whole afternoon. C++ Primer only give a code like
const char cp[]="jkasdkasjsad";
static_cast<string>(cp);
Could someone tell me is my code legal? Could I call it "cast away const" since no "const" before "string a"?
Any well-defined type conversion, other than those involving low-level const, can be
requested using a static_cast. For example, we can force our expression to use
floating-point division by casting one of the operands to double:
I was confused about the description above, what does "those involing low-level const" mean? Involving at left side or right side of an assignment?
Anyone can save me.. Many thanks!
Your string from cp array is being copied, string variable is not const
const char cp[] = "jkasdkasjsad";
std::string a = static_cast<std::string>(cp);
is equivalent to:
std::string ab = cp;
cp decays to pointer to first element of cp array
There is no real casting at all in this case.
static_cast<string>(cp);
is equivalent to call to string constructor
string(cp);
Temporary variable of type string constructed from cp will be returned from static_cast. Since, I think we talk about std::string, than this constructor will be called
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. The length of the
string is determined by the first null character.
Your code is perfectly legal according to the clause 5.2.9/4 of C++ standard:
An expression e can be explicitly converted to a type T using a
static_cast of the form static_cast<T>(e) if the declaration T t(e);
is well-formed, for some invented temporary variable t (8.5). The
effect of such an explicit conversion is the same as performing the
declaration and initialization and then using the temporary variable
as the result of the conversion.
For your example T is std::string, e is cp. There is no casting away constness because of new object creation. Compare with this:
char* p = static_cast<char*>(cp); // error
Related
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:
For instance, lets say I have
std::string str = 0;
Is the 0 being converted to a const char*?
Is it being coverted to a char* and then to a const char* when it's passed to the constructor?
I understand initializing a char* to 0 is undefined behavior, and as far as I know the same goes for const char*, but I don't understand the process of what's going on when I pass 0 to the std::string constructor.
Edit: I was wrong.
You are correct in your guessing.
If we look at e.g. this std::string constructor reference we can see that the only suitable constructor is number 5. Therefore your definition is equal to
std::string str = std::string(0);
And as noted in the reference:
The behavior is undefined if s does not point at an array of at least Traits::length(s)+1 elements of CharT, including the case when s is a null pointer.
[Emphasis mine]
So yes it constructs a std::string from the null-pointer which is indeed UB.
I understand initializing a char* to 0 is undefined behavior
You understand wrong. A 0 literal can be converted to a null pointer constant of any pointer type. There's nothing undefined there. The issues come when there's overloading involved, and the 0 can be converted not just to a pointer, but to another integral type. But that conversion itself is not problematic on its own.
Which brings us to what std::string str = 0; does. It initializes str, a class type, from 0. So we need to examine constructors, the only applicable one for 0 is this one:
basic_string( const CharT* s,
const Allocator& alloc = Allocator() );
So it indeed initializes str from a null pointer. And that is what's undefined.
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.
struct A {
char f1() {return *c;} // 1) f1() is rvalue
char * f2(){return c;} // 2) f1() is rvalue
char & f3(){return *c;} // 3) f1() is lvalue
char * c;
};
int main () {
char * a = "string"; // 4) it can be readable, when writed to a, problem will crash, why ?
const char * ab = "string"; // 5) ab is READ-ONLY
}
Questions
a) For line 2) why function with pointer return type is rvalue, not a lvalue ?
b) For line 4) and 5), what is really the type of "string" ? A "const char *" ? More intersting to know, which part of memory it locates in ?
why function with pointer return type is rvalue, not a lvalue ?
Because a pointer is an object, and the result of a function call is always an rvalue if the return type is an object type. Dereferencing the pointer will give an lvalue denoting the object it points to.
when writed to a, problem will crash, why ?
Because string literals are const, and writing to them gives undefined behaviour. In modern C++, there's no implicit conversion to non-const char*, so your example wouldn't compile. Historically, that dodgy conversion was allowed for arcane reasons. I suggest you consider using a more up-to-date compiler, specifying C++11 or C++14 as the language dialect, to prevent this nastiness. At the very least, enable compiler warnings.
what is really the type of "string" ?
An array of constant characters, large enough for the characters in the string literal plus a zero terminator. In this case, char const[7].
More intersting to know, which part of memory it locates in ?
String literals are constant, and have static storage duration, so are typically stored in a read-only data segment. It's unspecified whether two literals with the same value refer to the same static array, or two separate ones.
a)line 2 return back some rvalue in type (char*).It is rvalue of (char*).char* is just a type like int or char.in line 3 it is lvalue because there is (char&) and it sends back the reference(lvalue) .
b)"string" is a const char* variable because of that you cannot read or write with char* ,if you try , you trigger undefined behaviour.
char f1() {return *c;} // 1) f1() is rvalue
*c is the value the pointer c is pointing to
char * f2(){return c;} // 2) f1() is rvalue
f2() now points to the address pointed by c, that is now *f2() holds the value pointed to by c.
when writed to a, problem will crash, why ?
Because Attempting to modify a string literal results in undefined behavior in the words of cppreference. On a related note, assigning a string literal to a non-const char pointer is deprecated in c++03 and has been disallowed since c++11.
a) You're probably referring to the value returned by the function. A value returned by copy is always an rvalue.
b) The literal "string" is of type const char[7] and has static storage duration. It is typically stored in a read only data segment such as .rodata but that's not specified by the language.
a) Function char* f2() returns a value (roughly speaking, a number representing a memory address). Is the same of function char f1() that returns a value (another number).
Function char& f3(), on the other hand, returns a reference to a variable.
b) As Anton Savin pointed out, "string" is a const char[7] and for modern compilers (C++11) the first assignment is illegal. The memory location of the first character 's' is easily readable into ab.
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)