When I loop over a temporary std::string
(rvalue?) using a range-based for loop, there seems to be an extra character, the null terminator \0
.
When the string is not temporary (lvalue instead?), there is no extra character. Why?
std::map<char, int> m;
for (char c : "bar") m[c] = 0;
for (auto [c, f] : m) {
if (c == '\0') std::cout << "this is a null char, backward slash zero" << std::endl;
std::cout << c << std::endl;
}
Output:
this is a null char, backward slash zero
a
b
r
(note the empty line, where the \0
is being printed)
Compared to:
std::map<char,int> m;
std::string s = "bar";
for (char c : s) m[c] = 0;
for (auto [c, f] : m) {
if (c == '\0') std::cout << "this is a null char, backward slash zero" << std::endl;
std::cout << c << std::endl;
}
Output:
a
b
r
CodePudding user response:
Because "bar"
is not a std::string
, but a char
array (const char[4]
) containing 4 elements, including the last null character. I.e. c-style string literal:
The null character (
'\0'
,L'\0'
,char16_t()
, etc) is always appended to the string literal: thus, a string literal"Hello"
is aconst char[6]
holding the characters'H'
,'e'
,'l'
,'l'
,'o'
, and'\0'
.
For temporary std::string
s it will work as you expected, i.e. no null character contained.
for (char c : std::string{"bar"}) m[c] = 0;
Or
using namespace std::string_literals;
for (char c : "bar"s) m[c] = 0;
BTW as @HolyBlackCat suggested you can also use std::string_view
(since C 17) which won't include the null-terminated character when constructed from a c-style string literal. E.g.
for (char c : std::string_view{"bar"}) m[c] = 0;
Or
using namespace std::literals;
for (char c : "bar"sv) m[c] = 0;