-1

I have a constant string value

std::string name_to_use = "";

I need to use this value in just one place, calling the below function on it

std::wstring foo (std::string &x) {...};
// ...
std::wstring result = foo (name_to_use);

I can simply not declare the variable and use a string literal in the function call instead, but to allow easy configuration of name_to_use I decided to declare at the beginning of the file.

Now, since I am not really modifying name_to_use I thought why not use a #define preprocessing directive so I do not have to store name_to_use as a const anywhere in memory while the main program runs continuously (a GUI is displayed).

It worked fine, but then I came across constexpr. A user on stackoverflow has said to use it instead of #define as it is a safer option.

However, constexpr std::string name_to_use is still going to leak memory in this case right? Since it's not actually replacing occurrences of name_to_use with a value but holding a reference to it computed at compile time (which does not offer me any benefit here anyway, if I'm not mistaken?).

Ethicist
  • 791
  • 2
  • 7
  • 23
  • 1
    Your code doesn't compile because the function expects a non-const object. `constexpr std::string` also won't compile, since compile-time heap allocations can't persist until runtime. `const std::string` is an option, but it'll force a heap allocation at the program startup. I would modify the function to accept an `std::string_view`, and use a `constexpr std::string_view name_to_use = "";`. – HolyBlackCat Sep 01 '22 at 22:04
  • *"so I do not have to store name_to_use as a const anywhere in memory while*" - How do you reckon an executable can use a string that isn't anywhere? – StoryTeller - Unslander Monica Sep 01 '22 at 22:05
  • @HolyBlackCat this was just a quick mockup the const isn't supposed to be there – Ethicist Sep 01 '22 at 22:07
  • @StoryTeller-UnslanderMonica apologies I meant to say I don't want to have something continuously stored in memory when I can just create a string literal at will and it not existing beyond the enclosing scope – Ethicist Sep 01 '22 at 22:08
  • 1
    *"I don't want to have something continuously stored in memory"* It doesn't work like that. To create a string at runtime, the characters need to be permanently stored somewhere prior to that. You can avoid *copying* those characters elsewhere, but the single copy will always be there. – HolyBlackCat Sep 01 '22 at 22:18
  • @HolyBlackCat if I am just passing one to a function like `foo("my string")` is that still storing it permanently somewhere (is that what you're saying)? After foo processes that string and returns, why should the characters in "my string" have to be have been stored permanently somewhere?? Why does it not work like that? What's the reason they need to be permanently stored if they are only required temporarily? – Ethicist Sep 01 '22 at 22:27
  • Even if those characters are in-line in code, that in itself is *somewhere*. How else can a program know anything about the string? – StoryTeller - Unslander Monica Sep 01 '22 at 22:29
  • 1
    To abuse Lovecraft, No `new`, no leak. Know `new`, know leak. – user4581301 Sep 01 '22 at 22:30
  • Yes, with `foo("my string")` you have one permanent copy of the string, and, depending on the parameter type, possibly a second copy for as long as the function is running (there's the copy if it's a `std::string`, but no copy for `std::string_view` or `const char *`). *"After foo processes that string and returns, why should the characters in "my string" have to be have been stored"* There's no reason to keep them after that, but I don't think any compiler bothers with that. Even if it detects that the string is no longer needed, reusing the small space it used to occupy would be tricky. – HolyBlackCat Sep 01 '22 at 22:38
  • @HolyBlackCat thanks, didn't know that. It's something we need to manage ourselves then I guess. About `std::string_view` as the parameter type though, since I'm using a reference with `&x` as the parameter should it not use the same string object instead of a copy? Additionally, if it does make a permanent copy what should I do to deallocate it after use, if the compiler does not bother with it? Would I just do` std::string *name_to_use;` `name_to_use = malloc(sizeof(std::string));` `*name_to_use = "my string"; foo(name_to_use);` and call `free(name_to_use)` at the end of foo? – Ethicist Sep 01 '22 at 22:44
  • *"should it not use the same string"* Same string as what string? Remember that `"..."` is a char array, not `std::string`. To pass it to your function, you'd have to construct a temporary `std::string` with a copy of the characters. (Which doesn't happen with `string_view`). *"what should I do to deallocate it"* Nothing. Maybe I didn't get my point across: creating a `std::string` will produce a temporary copy of the characters, and then automatically delete it when the string dies. There's only one copy that's permanent (the literal itself), and you can't do anything about it. – HolyBlackCat Sep 01 '22 at 22:48
  • `name_to_use = malloc(sizeof(std::string));` is almost immediately fatal. `malloc` provides raw, uninitialized memory, so you get enough storage for a `std::string`, but you do not get a constructed `std::string`. `*name_to_use = "my string";` will likely trip over one or more of the uninitialized values that `string` expects to have been correctly set and die, but if by bad luck the program doesn't crash, bad smurf has happened and the program is going to be unstable. – user4581301 Sep 01 '22 at 22:50
  • @HolyBlackCat when I said *should it not use the same string* I was referring to the string passed to the function. I was asking why if I have `foo(std::string& x)` would it create a second copy inside the function **if it's a reference** (meaning the string or rather array of chars itself rather than a copy of it). The rest I can understand, thoroughly helpful. *"to pass it to your function you'd have to construct a temporary std::string"* this means that that std::string is being constructed automatically unless I specify the parameter as a string_view? – Ethicist Sep 01 '22 at 23:08
  • For starters, `foo("...")` doesn't compile if the reference is not const. If it's const, then yes, a temporary (copy) is created, and the reference refers to it. – HolyBlackCat Sep 01 '22 at 23:17

2 Answers2

2

If you #define it to "", then at each call there'll be a conversion from c-string to std::string, which is pretty inefficient. However, you can (usually) pass macro defines as arguments to compiler, which helps customization. Even in that case, it makes sense to write the static constexpr std::string name_to_use.

With static constexpr std::string name_to_use = ...;, the problem of conversion goes away (likely done compile-time). Don't expect the compiler not to do optimizations - if it's a compile-time string, it might happen that the entire function is optimized away (but still, the object will exists and the code will adhere to the as-if rule).

To combine the two, you can do:

#ifdef NAME_TO_USE
constexpr const std::string = # NAME_TO_USE;
#else
constexpr const std::string = "";
#endif

Also, as others said, please consider std::string_view to avoid allocation.

lorro
  • 10,687
  • 23
  • 36
  • "conversion from c-string to std::string," why does that matter if it's being done before compile parsing, if I understand correct it would not affect performance at runtime? Also I would appreciate an explanation on what that code is doing -- looks like it first checks if NAME_TO_USE is defined and if it is, it defines an unnamed constexpr?? – Ethicist Sep 01 '22 at 23:13
  • 1
    Say you use it in a loop. Depending on compiler, macro version might need the conversion (and thus allocation & deallocation) 1M times if the loop goes 1M times. `constexpr` only once. – lorro Sep 02 '22 at 06:57
-2

The user is saying well, and you did understand right.

Using the constexpr method will allocate a constant when the macro will just replace itself at compile time. The only benefit of the first is that it is typed, and can make your code a little bit safer when you compile it.

This being said, the choice is yours. Do you want to have a non-typed macro that doesn't add any operation on run-time, or a typed constant that use a little bit of memory at parsing ?

yaourtiere
  • 36
  • 6