Why can I assign characters to string objects but not to vectors of string objects?
C++ Primer says that character and string literals may be converted to
string
s.
C-style string literals can convert to std::string
implicitly, but char
s can't. That's different from assignment.
s = 's';
works because std::string
has an overloaded operator=
taking char
.
- Replaces the contents with character
ch
as if byassign(std::addressof(ch), 1)
svec = {'a', 'b'};
doesn't work because std::vector
only has overloaded operator=
s taking std::vector
or std::initializer_list
, both of them can't be constructed from braced-init-list {'a', 'b'}
. You might think the overload taking std::initializer_list<std::string>
could be used for this case, but char
can't be converted to std::string
implicitly (std::string
doesn't have such converting constructor taking a char
), then std::initializer_list<std::string>
failed to be constructed from {'a', 'b'}
.
As the workaround, you can change the code to
svec = {"a", "b"};
"a"
is of type const char[2]
and decays const char*
, which could be converted to std::string
implicitly (via the std::string
's converting constructor taking const char*
), then std::initializer_list<std::string>
gets constructed from {"a", "b"}
and passed to std::vector::operator=
. Of course svec = {std::string("a"), std::string("b")};
(or svec = {"a"s, "b"s};
) works too, std::initializer_list<std::string>
will be constructed directly without such implicit conversion to std::string
.
The assignment of a character literal in the first expression works because the std::string
class has an overload for the assignment operator that takes char
.
The character literal arguments in the second expression cannot be implicitly converted to strings, like string literals can (i.e. svec = {"a", "b"}
), because std::string
has a constructor for const char*
but not for char
:
The expression:
svec = {"a", "b"};
uses the constructor
string (const char* s);
The expression:
svec = {'a', 'b'};
can't work because no such constructor exists that takes a single character argument.
What it does have is a constructor that takes an initializer_list
(as you can see in the previous link):
string (initializer_list<char> il);
Available since C++11.
So to initialize std::string
with character literals you need to use curly brackets (i.e. braced initializer list):
std::vector<std::string> svec;
svec = {{'a'}, {'b'}};
This will, as you know, initialize 2 strings in the first 2 positions of the vector one contains "a"
and the other "b"
.
For a single string in the first position of the vector you can use:
svec = {{'a','b'}};
The key to understanding this is initializer lists.
First, note that this does not work:
std::string s('a');
but this does:
std::string s{'a'};
The reason is that the first one would require a ctor that takes a single char
, but std::string
does not have such a constructor. The second, on the other hand, creates an initializer_list<char>
, for which std::string
does have a ctor.
The exact same reasoning applies to
std::vector<std::string>> vs{ 'a', 'b' };
versus
std::vector<std::string>> vs{ {'a'}, {'b'} };
The first wants to use a non-existent std::string
ctor taking a char
, and the second uses initializer lists.
As for the original code, the reason that
std::string s;
s = 'a';
works is that while std::string
lacks a ctor taking a char
, it does have an assignment operator which takes a char
.