How to declare constexpr C string? - c++

I think i quite understand how to use the keyword constexpr for simple variable types, but i'm confused when it comes to pointers to values.
I would like to declare a constexpr C string literal, which will behave like
#define my_str "hello"
That means the compiler inserts the C string literal into every place where i enter this symbol, and i will be able to get its length at compile-time with sizeof.
Is it constexpr char * const my_str = "hello";
or const char * constexpr my_str = "hello";
or constexpr char my_str [] = "hello";
or something yet different?

Is it constexpr char * const my_str = "hello";
No, because a string literal is not convertible to a pointer to char. (It used to be prior to C++11, but even then the conversion was deprecated).
or const char * constexpr my_str = "hello";
No. constexpr cannot go there.
This would be well formed:
constexpr const char * my_str = "hello";
but it does not satify this:
So that i will be able to get its length at compile-time with sizeof, etc.
or constexpr char my_str [] = "hello";
This is well formed, and you can indeed get the length at compile time with sizeof. Note that this size is the size of the array, not the length of the string i.e. the size includes the null terminator.

In C++17, you can use std::string_view and string_view_literals
using namespace std::string_view_literals;
constexpr std::string_view my_str = "hello, world"sv;
Then,
my_str.size() is compile time constant.

Related

Why does const have to be added to constexpr for a string literal declaration?

This declaration:
char constexpr *const s = "hello";
Fails with this error:
g++ -g -Wall -Werror -std=c++17 test.cc -o test
test.cc:8:31: error: ISO C++11 does not allow conversion from string literal to 'char *const' [-Werror,-Wwritable-strings]
char constexpr *const s = "hello";
But if I add const to constexpr, the compiler is happy:
char const constexpr *const s = "hello";
Compilation:
g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
hello
This seems unintuitive to me. Why does const need to decorate constexpr? Doesn't constexpr imply const? If it's a compiler constant, how is it not a constant in some other sense? Is it possible for something to be constexpr but change in some other way such that is not constant?
Here's a minimal godbolt:
https://godbolt.org/z/sSQMVa
Update:
StoryTeller's answer has the key to understanding this. I've accepted his answer, but I'll expand on it here in case that's helpful for someone else trying to understand this. When interacting with const, I'm used to thinking of const as applying to the item on the left of it. Thus:
char a[] = "hello";
char * const s = a;
s[0] = 'H'; // OK
s = "there"; // Compiler error.
Here, char * const s means that the pointer, s, is const while the characters it dereferences to are modifiable. On the other hand:
char const * s = "hello";
a[0] = 'H'; // Compiler error
s = "there"; // OK
In this case, char const * s means that the characters that s points to are const, not the pointer.
OK, most people who have worked with const and pointers understand all that. Where I got thrown off is that I assumed that constexpr would work this way as well. That is, given this:
char constexpr * const s = "hello";
I thought that would mean that the pointer is const (it is) and that the characters themselves would be const and constexpr. But the syntax doesn't work that way. Rather, the constexpr in this case:
Does not apply to the characters, but rather...
applies to s itself, which is a pointer, and...
therefore is redundant with the const after the pointer.
Thus, in this case, there is no const being declared on the characters. Indeed, if I remove the constexpr entirely I get the exact same error:
char * const s = "hello"; // Produces same error as char constexpr * const s = "hello";
This, however, works:
constexpr char const * s = "hello";
The above has what we want and it means:
The characters are const via const
And the pointer s is const and a compile time constant via constexpr
Doesn't constexpr imply const?
It does, on the object being declared, in your case s. The result of applying constexpr is the object
char *const s;
It's still declared to point at a non-const object. Only the address has to be a constant expression. Which means it must be to an object with static storage duration.
Is it possible for something to be constexpr but change in some other way such that is not constant?
No. But then again, it's not the object being declared constexpr that is allowed to change here. For instance
static char foo[] = "abc"; // Not a constant array
constexpr char * s = foo; // But the address is still a valid initializer.
Is a valid pair of declarations.
const applies to the thing on its left, or if nothing is there then to its right.
In char *const s = "hello";, the const is applied to the *, not to the char, so s is a const pointer to non-const char data. However, a string literal is const char data (in this case, "hello" is a const char[6]). You can't have a pointer to non-const data that is actually pointing at const data, that would allow the const data to be modifiable, which is undefined behavior if something actually tried to modify the data. That is what the compiler error is complaining about.
So, you need a pointer to const char data instead:
char const *const s = "hello";
Or:
const char *const s = "hello";
The constexpr just makes the s variable available for evaluation at compile-time.
Thanks for StoryTeller's answer and Firebush's explanation which teach me a lot.
And the problem lead me here is about const with array , I have done some simple test like Firebush.
Regard for array, const always prevent to modify specific dimension instead of all content which I think maybe help for some one, so I post test code and comments here.
char* const strs1[] = {"uu", "vv"}; // protected 1st dimension
const char* strs2[] = {"ww", "xx"}; // protected 2nd dimension
char* strs3[] = {"yy", "zz"};
strs1[0] = "aa"; // error, try to modify 1st dimension
strs1[0][0] = 'a';
strs2[0] = "aa";
strs2[0][0] = 'a'; // error, try to modify 2nd dimension
strs1 = strs3; // error, try to modify 1st dimension
strs2 = strs3; // error, try to modify 2nd dimension
The truth is all above, the biggest regret is miss a short and effective summary in several words to make all of us never forget the usage of const.
static constexpr auto NONE = "none";

modify const char * vs char * content in easy way

im realy confused about const char * and char *.
I know in char * when we want to modify the content, we need to do something like this
const char * temp = "Hello world";
char * str = new char[strlen(temp) + 1];
memcpy(str, temp, strlen(temp));
str[strlen(temp) + 1] = '\0';
and if we want to use something like this
char * str = "xxx";
char * str2 = "xts";
str = str2;
we get compiler warning. it's ok I know when i want to change char * I have to use something memory copy. but about const char * im realy confused. in const char * I can use this
const char * str = "Hello";
const char * str2 = "World";
str = str2; // and now str is Hello
and I have no compiler error ! why ? why we use memory copy when is not const and in const we only use equal operator ! and done !... how possible? is it ok to just use equal in const? no problem happen later?
As other answers say, you should distinguish pointers and bytes they point to.
Both types of pointers, char * and const char *, can be changed, that is, "redirected" to point to different bytes. However, if you want to change the bytes (characters) of the strings, you cannot use const char *.
So, if you have string literals "Hello" and "World" in your program, you can assign them to pointers, and printing the pointer will print the corresponding literal. However, to do anything non-trivial (e.g. change Hello to HELLO), you will need non-const pointers.
Another example: with some pointer manipulation, you can remove leading bytes from a string literal:
const char* str = "Hello";
std::cout << str; // Hello
str = str + 2;
std::cout << str; // llo
However, if you want to extract a substring, or do any other transformation on a string, you should reallocate it, and for that you need a non-const pointer.
BTW since you are using C++, you can use std::string, which makes it easier to work with strings. It reallocates strings without your intervention:
#include <string>
std::string str("Hello");
str = str.substr(1, 3);
std::cout << str; // ell
This is a confusing hangover from the days of early C. Early C didn't have const, so string literals were "char *". They remained char * to avoid breaking old code, but they became non-modifiable, so const char * in all but name. So modern C++ either warns or gives an error (to be strictly conforming) when the const is omitted.
Your memcpy missed the trailing nul byte, incidentally. Use strcpy() to copy a string, that's the right function with the right name. You can create a string in read/write memory by use of the
char rwstring[] = "I am writeable";
syntax.
That is cause your variables are just a pointers *. You're not modifiying their contents, but where they are pointing to.
char * a = "asd";
char * b = "qwe";
a = b;
now you threw away the contents of a. Now a and b points to the same place. If you modify one, both are modified.
In other words. Pointers are never constants (mostly). your const predicate in a pointer variable does not means nothing to the pointer.
The real difference is that the pointer (that is not const) is pointing to a const variable. and when you change the pointer it will be point to ANOTHER NEW const variable. That is why const has no effect on simple pointers.
Note: You can achieve different behaviours with pointers and const with more complex scenario. But with simple as it, it mostly has no effect.
Citing Malcolm McLean:
This is a confusing hangover from the days of early C. Early C didn't have const, so string literals were "char *". They remained char * to avoid breaking old code, but they became non-modifiable, so const char * in all but name.
Actually, string literals are not pointers, but arrays, this is why sizeof("hello world") works as a charm (yields 12, the terminating null character is included, in contrast to strlen...). Apart from this small detail, above statement is correct for good old C even in these days.
In C++, though, string literals have been arrays of constant characters (char const[]) right from the start:
C++ standard, 5.13.5.8:
Ordinary string literals and UTF-8 string literals are also referred to as narrow string literals. A narrow string literal has type “array of n const char”, where n is the size of the string as defined below, and has static storage duration.
(Emphasised by me.) In general, you are not allowed to assign pointer to const to pointer to non-const:
char const* s = "hello";
char ss = s;
This will fail to compile. Assigning string literals to pointer to non-const should normally fail, too, as the standard explicitly states in C.1.1, subclause 5.13.5:
Change: String literals made const.
The type of a string literal is changed from “array of char” to “array of const char”.
[...]char* p = "abc"; // valid in C, invalid in C++
Still, string literal assignement to pointer to non-const is commonly accepted by compilers (as an extension!), probably to retain compatibility to C. As this is, according to the standard, invalid, the compiler yields a warning, at least...

Why does "auto" declare strings as const char* instead of std::string?

I made a template which adds the data it is given. If I use it like this, the compiler declares in_1 and in_2 as const char *, and the code doesn't compile.
#include <iostream>
using namespace std;
template <class T>
T addstuff(T part_1, T part_2){
return(part_1+part_2);
}
int main(int argc, char const *argv[])
{
auto in_1="Shut ";
auto in_2="up.";
cout<<addstuff(in_1, in_2)<<endl;
return 0;
}
If I declare in_1 and in_2 std::string, it works like a charm.
Why can't (or doesn't) the compiler declare those strings automatically std::string?
The reason you can't "write" to your auto variable is that it's a const char * or const char [1], because that is the type of any string constant.
The point of auto is to resolve to the simplest possible type which "works" for the type of the assignment. The compiler does not "look forward to see what you are doing with the variable", so it doesn't understand that later on you will want to write into this variable, and use it to store a string, so std::string would make more sense.
You code could be made to work in many different ways, here's one that makes some sense:
std::string default_name = "";
auto name = default_name;
cin >> name;
If you use string literals, auto will work as expected.
In C++14, C++17 or C++20, you can place an s after the quotes, and it will create a std::string instead of a const char* string.
This can be used together with auto to create a std::string:
auto hello = "hello"s;
String literals are not enabled by default. One way of enabling string literals is to place the following at the top of the source file:
#include <string>
using namespace std::string_literals;
As an example, this loop works for std::string (with s added to the string literal), but not for const char* type string literals:
for (auto &x : hello) {
std::cout << "letter: " << x << std::endl;
}
Here is the cppreference page for the ""s operator.
Because string literals have type const char[N+1], not std::string.
This is just a fact of the language.
They could have made it so that auto has a special case for string literals, but that would be inconsistent, surprising and of very little benefit.
auto will declare the variable as the compile-time type of the expression you initialize it to.
String literals are of type const char*, not std::string.

const string vs. #define

i need to share some strings in my c++ program. should i use #define or const string? thanks
mystring1.h
#define str1 "str1"
#define str2 "str2"
Or
mystring2.h
extern const string str1;
extern const string str2;
mystring.cpp
const string str1 = "str1";
const string str2 = "str2";
Prefer the second option. If you use the first option (preprocessor), you are limiting your flexibility with the object.
Consider the following... You won't be able to compare strings this way:
if (str1 == "some string")
{
// ...
}
If it is C++ instead of C, you should really use some variable instead of a preprocessor macro. The former is clearer than the latter. Furthermore, if you use C++17, you can use inline variables:
inline const std::string str = "foobar";
or
// constexpr is implicitly inline
constexpr char str0[] = "foobar";
constexpr const char* str1 = "foobar";
constexpr std::string_view str2 = "foobar";
This is also clearer than using extern and can be used in header-only APIs as well.
If it's C++, you should use the C++ Standard Library's std::string. It's much more clear than a preprocessor macro, it will have a single location in memory when it's defined, and it has all the extra functionality of std::string instead of only pointer comparisons as is the case with the implicit const char* that are created with a preprocessor macro.
To take OO advantage of c++, I would say use struct/class.
header:
struct Constants {
static const string s1;
static const string s2;
};
cpp:
const string Constants::s1 = "blah1";
const string Constants::s2 = "blah2";
To reference:
cout << Constants::s1 << endl;
If you don't have to use the preprocessor don't!
If these strings are needed in a resource editor or a manifest or something you might have to.
You could also just use a const char* string for constant data and not a string object, since the object will need to be initialised at the start of the program with the constant data anyway. Do this if you're not going to be doing much with strings but just displaying them or printing them out as is.
So:
extern const char * str1;
and
const char * str1 = "str1";
I would suggest use of functions.
extern const std::string& str1();
extern const std::string& str2();
This gives you more flexibility in how you get those strings in the .cpp file.
Also consider the issue of non-POD static construction and destruction order, as described in the Google C++ style guide.
An alternative is to use:
const char str1[] = "str1";
const char str1[] = "str2";

C++ deprecated conversion from string constant to 'char*'

I have a class with a private char str[256];
and for it I have an explicit constructor:
explicit myClass(char *func)
{
strcpy(str,func);
}
I call it as:
myClass obj("example");
When I compile this I get the following warning:
deprecated conversion from string constant to 'char*'
Why is this happening?
This is an error message you see whenever you have a situation like the following:
char* pointer_to_nonconst = "string literal";
Why? Well, C and C++ differ in the type of the string literal. In C the type is array of char and in C++ it is constant array of char. In any case, you are not allowed to change the characters of the string literal, so the const in C++ is not really a restriction but more of a type safety thing. A conversion from const char* to char* is generally not possible without an explicit cast for safety reasons. But for backwards compatibility with C the language C++ still allows assigning a string literal to a char* and gives you a warning about this conversion being deprecated.
So, somewhere you are missing one or more consts in your program for const correctness. But the code you showed to us is not the problem as it does not do this kind of deprecated conversion. The warning must have come from some other place.
The warning:
deprecated conversion from string constant to 'char*'
is given because you are doing somewhere (not in the code you posted) something like:
void foo(char* str);
foo("hello");
The problem is that you are trying to convert a string literal (with type const char[]) to char*.
You can convert a const char[] to const char* because the array decays to the pointer, but what you are doing is making a mutable a constant.
This conversion is probably allowed for C compatibility and just gives you the warning mentioned.
As answer no. 2 by fnieto - Fernando Nieto clearly and correctly describes that this warning is given because somewhere in your code you are doing (not in the code you posted) something like:
void foo(char* str);
foo("hello");
However, if you want to keep your code warning-free as well then just make respective change in your code:
void foo(char* str);
foo((char *)"hello");
That is, simply cast the string constant to (char *).
There are 3 solutions:
Solution 1:
const char *x = "foo bar";
Solution 2:
char *x = (char *)"foo bar";
Solution 3:
char* x = (char*) malloc(strlen("foo bar")+1); // +1 for the terminator
strcpy(x,"foo bar");
Arrays also can be used instead of pointers because an array is already a constant pointer.
Update: See the comments for security concerns regarding solution 3.
A reason for this problem (which is even harder to detect than the issue with char* str = "some string" - which others have explained) is when you are using constexpr.
constexpr char* str = "some string";
It seems that it would behave similar to const char* str, and so would not cause a warning, as it occurs before char*, but it instead behaves as char* const str.
Details
Constant pointer, and pointer to a constant. The difference between const char* str, and char* const str can be explained as follows.
const char* str : Declare str to be a pointer to a const char. This means that the data to which this pointer is pointing to it constant. The pointer can be modified, but any attempt to modify the data would throw a compilation error.
str++ ; : VALID. We are modifying the pointer, and not the data being pointed to.
*str = 'a'; : INVALID. We are trying to modify the data being pointed to.
char* const str : Declare str to be a const pointer to char. This means that point is now constant, but the data being pointed too is not. The pointer cannot be modified but we can modify the data using the pointer.
str++ ; : INVALID. We are trying to modify the pointer variable, which is a constant.
*str = 'a'; : VALID. We are trying to modify the data being pointed to. In our case this will not cause a compilation error, but will cause a runtime error, as the string will most probably will go into a read only section of the compiled binary. This statement would make sense if we had dynamically allocated memory, eg. char* const str = new char[5];.
const char* const str : Declare str to be a const pointer to a const char. In this case we can neither modify the pointer, nor the data being pointed to.
str++ ; : INVALID. We are trying to modify the pointer variable, which is a constant.
*str = 'a'; : INVALID. We are trying to modify the data pointed by this pointer, which is also constant.
In my case the issue was that I was expecting constexpr char* str to behave as const char* str, and not char* const str, since visually it seems closer to the former.
Also, the warning generated for constexpr char* str = "some string" is slightly different from char* str = "some string".
Compiler warning for constexpr char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *const'
Compiler warning for char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *'.
Tip
You can use C gibberish ↔ English converter to convert C declarations to easily understandable English statements, and vice versa. This is a C only tool, and thus wont support things (like constexpr) which are exclusive to C++.
In fact a string constant literal is neither a const char * nor a char* but a char[]. Its quite strange but written down in the c++ specifications; If you modify it the behavior is undefined because the compiler may store it in the code segment.
Maybe you can try this:
void foo(const char* str)
{
// Do something
}
foo("Hello")
It works for me
I solve this problem by adding this macro in the beginning of the code, somewhere. Or add it in <iostream>, hehe.
#define C_TEXT( text ) ((char*)std::string( text ).c_str())
I also got the same problem. And what I simple did is just adding const char* instead of char*. And the problem solved. As others have mentioned above it is a compatible error. C treats strings as char arrays while C++ treat them as const char arrays.
For what its worth, I find this simple wrapper class to be helpful for converting C++ strings to char *:
class StringWrapper {
std::vector<char> vec;
public:
StringWrapper(const std::string &str) : vec(str.begin(), str.end()) {
}
char *getChars() {
return &vec[0];
}
};
The following illustrates the solution, assign your string to a variable pointer to a constant array of char (a string is a constant pointer to a constant array of char - plus length info):
#include <iostream>
void Swap(const char * & left, const char * & right) {
const char *const temp = left;
left = right;
right = temp;
}
int main() {
const char * x = "Hello"; // These works because you are making a variable
const char * y = "World"; // pointer to a constant string
std::cout << "x = " << x << ", y = " << y << '\n';
Swap(x, y);
std::cout << "x = " << x << ", y = " << y << '\n';
}