Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #68209 > unrolled thread
| Started by | Ralf Goertz <me@myprovider.invalid> |
|---|---|
| First post | 2019-09-04 14:09 +0200 |
| Last post | 2019-09-04 14:43 +0200 |
| Articles | 13 — 8 participants |
Back to article view | Back to comp.lang.c++
Initialization of std::vector<std::string> throws Ralf Goertz <me@myprovider.invalid> - 2019-09-04 14:09 +0200
Re: Initialization of std::vector<std::string> throws Sam <sam@email-scan.com> - 2019-09-04 08:20 -0400
Re: Initialization of std::vector<std::string> throws Ralf Goertz <me@myprovider.invalid> - 2019-09-04 14:33 +0200
Re: Initialization of std::vector<std::string> throws Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> - 2019-09-05 23:26 -0400
Re: Initialization of std::vector<std::string> throws Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-09-18 08:37 -0700
Re: Initialization of std::vector<std::string> throws Ralf Goertz <me@myprovider.invalid> - 2019-11-22 12:12 +0100
Re: Initialization of std::vector<std::string> throws Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-11-22 13:44 +0000
Re: Initialization of std::vector<std::string> throws Ralf Goertz <me@myprovider.invalid> - 2019-11-22 16:03 +0100
Re: Initialization of std::vector<std::string> throws Bo Persson <bo@bo-persson.se> - 2019-11-22 16:22 +0100
Re: Initialization of std::vector<std::string> throws Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-11-22 16:13 +0000
Re: Initialization of std::vector<std::string> throws Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-28 13:44 -0800
Re: Initialization of std::vector<std::string> throws "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-11-22 16:30 +0100
Re: Initialization of std::vector<std::string> throws Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-04 14:43 +0200
| From | Ralf Goertz <me@myprovider.invalid> |
|---|---|
| Date | 2019-09-04 14:09 +0200 |
| Subject | Initialization of std::vector<std::string> throws |
| Message-ID | <qko9ig$97d$1@dont-email.me> |
Hi,
why does the declaration
std::vector<std::string> pt({{"One","Two"}});
throw:
terminate called after throwing an instance of 'std::length_error'
what(): basic_string::_M_create
whereas neither
std::vector<std::string> pt({{"One"}});
nor
std::vector<int> pt({{1,2}});
does? Full program:
#include <vector>
#include <string>
#include <iostream>
std::vector<std::string> pt({{"One","Two"}});
int main() {
for (auto p:pt) std::cout<<p<<std::endl;
}
No warning is given during compilation by gcc version 9.2.1 20190820
[toc] | [next] | [standalone]
| From | Sam <sam@email-scan.com> |
|---|---|
| Date | 2019-09-04 08:20 -0400 |
| Message-ID | <cone.1567599651.572642.18978.1004@monster.email-scan.com> |
| In reply to | #68209 |
[Multipart message — attachments visible in raw view] — view raw
Ralf Goertz writes:
> Hi,
>
> why does the declaration
>
> std::vector<std::string> pt({{"One","Two"}});
>
> throw:
>
> terminate called after throwing an instance of 'std::length_error'
> what(): basic_string::_M_create
Because this is undefined behavior.
> whereas neither
>
> std::vector<std::string> pt({{"One"}});
>
> nor
>
> std::vector<int> pt({{1,2}});
If you actually examine the end result the last declaration, you will
discover, to your surprise, a std::vector<int> with just one value, and that
would hopefully be a big honking clue as to what this is doing.
{ /* something */ } introduces a braced initialization list.
{ { /* something */ } } introduces a braced initialization list with one
value.
{ { "One", "Two" } } attempts to construct a single std::string with two
values. The only overloaded std::string constructor this matches is the one
that takes a beginning iterator and an ending iterator of a sequence that
forms the contents of the constructed std::string.
Since "One" and "Two" decay to two character pointers which do not comprise
a single sequence, you just nuked yourself from high orbit, hence the crash.
> No warning is given during compilation by gcc version 9.2.1 20190820
A compiler has no obligation to warn you when you're about to shoot yourself
in a foot. It would be nice if it did, and in many cases it does; but you
can't rely on your compiler to keep you from shooting yourself in a foot.
[toc] | [prev] | [next] | [standalone]
| From | Ralf Goertz <me@myprovider.invalid> |
|---|---|
| Date | 2019-09-04 14:33 +0200 |
| Message-ID | <qkoav3$97d$2@dont-email.me> |
| In reply to | #68210 |
Am Wed, 04 Sep 2019 08:20:51 -0400
schrieb Sam <sam@email-scan.com>:
> Ralf Goertz writes:
>
> > Hi,
> >
> > why does the declaration
> >
> > std::vector<std::string> pt({{"One","Two"}});
> >
> > throw:
> >
> > terminate called after throwing an instance of 'std::length_error'
> > what(): basic_string::_M_create
>
> Because this is undefined behavior.
>
> > whereas neither
> >
> > std::vector<std::string> pt({{"One"}});
> >
> > nor
> >
> > std::vector<int> pt({{1,2}});
>
> If you actually examine the end result the last declaration, you will
> discover, to your surprise, a std::vector<int> with just one value,
> and that would hopefully be a big honking clue as to what this is
> doing.
Hm, I did that and it failed to surprise me.
#include <vector>
#include <string>
#include <iostream>
std::vector<int> pt({{1,2}});
int main() {
for (auto p:pt) std::cout<<p<<std::endl;
}
gives:
1
2
here. Just as I expected.
> { /* something */ } introduces a braced initialization list.
>
> { { /* something */ } } introduces a braced initialization list with
> one value.
But didn't we need to use two braces for container intialization?
[toc] | [prev] | [next] | [standalone]
| From | Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> |
|---|---|
| Date | 2019-09-05 23:26 -0400 |
| Message-ID | <RlkcF.175481$Pr1.15721@fx01.iad> |
| In reply to | #68211 |
Ralf Goertz wrote:
> Am Wed, 04 Sep 2019 08:20:51 -0400
> schrieb Sam <sam@email-scan.com>:
>
>> Ralf Goertz writes:
>>
>>> Hi,
>>>
>>> why does the declaration
>>>
>>> std::vector<std::string> pt({{"One","Two"}});
>>>
>>> throw:
>>>
>>> terminate called after throwing an instance of 'std::length_error'
>>> what(): basic_string::_M_create
>>
>> Because this is undefined behavior.
>>
>>> whereas neither
>>>
>>> std::vector<std::string> pt({{"One"}});
>>>
>>> nor
>>>
>>> std::vector<int> pt({{1,2}});
>>
>> If you actually examine the end result the last declaration, you will
>> discover, to your surprise, a std::vector<int> with just one value,
>> and that would hopefully be a big honking clue as to what this is
>> doing.
>
> Hm, I did that and it failed to surprise me.
>
>
> #include <vector>
> #include <string>
> #include <iostream>
>
> std::vector<int> pt({{1,2}});
> int main() {
> for (auto p:pt) std::cout<<p<<std::endl;
> }
>
> gives:
> 1
> 2
my guess is the above pt declarator is roughly equivalent to
pt(vector<int>(initializer_list<int>(initializer_list<int>(1,2)))) --
which just happens to give you an expected result. I think it is
undefined or implementation-specific what default copy constructor does
on initializer_list because the data members of it are not defined by
the standard.
>
> here. Just as I expected.
>> { /* something */ } introduces a braced initialization list.
>>
>> { { /* something */ } } introduces a braced initialization list with
>> one value.
>
> But didn't we need to use two braces for container intialization?
>
for vector<int>, because there is constructor with count and value, you
need something like = { 1, 2}; (note =) for {1,2} to be interpreted as
initializer_list; for vector<string> you should be able to initialize
with {"One", "Two"} directly.
-Pavel
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2019-09-18 08:37 -0700 |
| Message-ID | <86blvh4nus.fsf@linuxsc.com> |
| In reply to | #68247 |
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
> Ralf Goertz wrote:
>
>> Am Wed, 04 Sep 2019 08:20:51 -0400
>> schrieb Sam <sam@email-scan.com>:
>>
>>> Ralf Goertz writes:
>>>
>>>> why does the declaration
>>>>
>>>> std::vector<std::string> pt({{"One","Two"}});
>>>>
>>>> throw:
>>>>
>>>> terminate called after throwing an instance of 'std::length_error'
>>>> what(): basic_string::_M_create
>>>
>>> Because this is undefined behavior.
>>>
>>>> whereas neither
>>>>
>>>> std::vector<std::string> pt({{"One"}});
>>>>
>>>> nor
>>>>
>>>> std::vector<int> pt({{1,2}});
>>>
>>> If you actually examine the end result the last declaration, you will
>>> discover, to your surprise, a std::vector<int> with just one value,
>>> and that would hopefully be a big honking clue as to what this is
>>> doing.
>>
>> Hm, I did that and it failed to surprise me.
>>
>>
>> #include <vector>
>> #include <string>
>> #include <iostream>
>>
>> std::vector<int> pt({{1,2}});
>> int main() {
>> for (auto p:pt) std::cout<<p<<std::endl;
>> }
>>
>> gives:
>> 1
>> 2
>
> my guess is the above pt declarator is roughly equivalent to
> pt(vector<int>(initializer_list<int>(initializer_list<int>(1,2)))) --
> which just happens to give you an expected result. I think it is
> undefined or implementation-specific what default copy constructor does
> on initializer_list because the data members of it are not defined by
> the standard.
This seems wrong to me. Please note I am not saying it IS wrong,
only that it seems to me to be wrong. More specifically I don't
think undefined behavior plays a role. I tried reading the
Standard to verify that but found it impenetrable in this area.
Can someone help find the relevant passages (and an accompanying
explanation would also be appreciated)?
>> here. Just as I expected.
>>
>>> { /* something */ } introduces a braced initialization list.
>>>
>>> { { /* something */ } } introduces a braced initialization list with
>>> one value.
>>
>> But didn't we need to use two braces for container intialization?
>
> for vector<int>, because there is constructor with count and value, you
> need something like = { 1, 2}; (note =) for {1,2} to be interpreted as
> initializer_list; for vector<string> you should be able to initialize
> with {"One", "Two"} directly.
I'm pretty sure what you say about vector<int> isn't right. A
declaration like
vector<int> foo { 10, 1 };
gives a vector of length 2, not a vector of length 10. If the
braces were parentheses
vector<int> bas ( 10, 1 );
we would get a vector of length 10, using the ( count, value )
constructor.
Disclaimer: the comments above are based solely on experimental
results, not on understanding what the Standard says about it.
[toc] | [prev] | [next] | [standalone]
| From | Ralf Goertz <me@myprovider.invalid> |
|---|---|
| Date | 2019-11-22 12:12 +0100 |
| Message-ID | <qr8frb$47q$1@dont-email.me> |
| In reply to | #68211 |
Am Wed, 4 Sep 2019 14:33:39 +0200
schrieb Ralf Goertz <me@myprovider.invalid>:
> Am Wed, 04 Sep 2019 08:20:51 -0400
> schrieb Sam <sam@email-scan.com>:
>
> > Ralf Goertz writes:
> >
> > > Hi,
> > >
> > > why does the declaration
> > >
> > > std::vector<std::string> pt({{"One","Two"}});
> > >
> > > throw:
> > >
> > > terminate called after throwing an instance of 'std::length_error'
> > > what(): basic_string::_M_create
> >
> > Because this is undefined behavior.
> >
> > > whereas neither
> > >
> > > std::vector<std::string> pt({{"One"}});
> > >
> > > nor
> > >
> > > std::vector<int> pt({{1,2}});
> >
> > If you actually examine the end result the last declaration, you
> > will discover, to your surprise, a std::vector<int> with just one
> > value, and that would hopefully be a big honking clue as to what
> > this is doing.
>
> Hm, I did that and it failed to surprise me.
>
>
> #include <vector>
> #include <string>
> #include <iostream>
>
> std::vector<int> pt({{1,2}});
> int main() {
> for (auto p:pt) std::cout<<p<<std::endl;
> }
>
> gives:
> 1
> 2
>
> here. Just as I expected.
> > { /* something */ } introduces a braced initialization list.
> >
> > { { /* something */ } } introduces a braced initialization list with
> > one value.
>
> But didn't we need to use two braces for container intialization?
Since nobody has explained that satisfactorily yet (also see Tim's
answer to Pavel) and I just stumbled over it again, I'd like to follow
up. In the following situation double braces are necessary. But why?
This kind of problem led to my original belief that for containers they
were always needed.
#include <vector>
#include <iostream>
struct Foo {
std::vector<int> v;
Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
};
int main() {
Foo f{{4,5}}; //double braces needed
for (auto r:f.v)
std::cout<<r<<std::endl;
return 0;
}
With single braces I get:
problem.cc: In function ‘int main()’:
problem.cc:10:14: error: no matching function for call to ‘Foo::Foo(<brace-enclosed initializer list>)’
10 | Foo f{4,5};
| ^
problem.cc:6:14: note: candidate: ‘Foo::Foo(std::vector<int>)’
6 | Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
| ^~~
problem.cc:6:14: note: candidate expects 1 argument, 2 provided
problem.cc:4:8: note: candidate: ‘Foo::Foo(const Foo&)’
4 | struct Foo {
| ^~~
problem.cc:4:8: note: candidate expects 1 argument, 2 provided
problem.cc:4:8: note: candidate: ‘Foo::Foo(Foo&&)’
problem.cc:4:8: note: candidate expects 1 argument, 2 provided
[toc] | [prev] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2019-11-22 13:44 +0000 |
| Message-ID | <20191122134409.33dffbcff5234841f8ea7411@cvine--nospam--.freeserve.co.uk> |
| In reply to | #69976 |
On Fri, 22 Nov 2019 12:12:43 +0100
Ralf Goertz <me@myprovider.invalid> wrote:
> Am Wed, 4 Sep 2019 14:33:39 +0200
> schrieb Ralf Goertz <me@myprovider.invalid>:
>
> > Am Wed, 04 Sep 2019 08:20:51 -0400
> > schrieb Sam <sam@email-scan.com>:
> >
> > > Ralf Goertz writes:
> > >
> > > > Hi,
> > > >
> > > > why does the declaration
> > > >
> > > > std::vector<std::string> pt({{"One","Two"}});
> > > >
> > > > throw:
> > > >
> > > > terminate called after throwing an instance of 'std::length_error'
> > > > what(): basic_string::_M_create
> > >
> > > Because this is undefined behavior.
> > >
> > > > whereas neither
> > > >
> > > > std::vector<std::string> pt({{"One"}});
> > > >
> > > > nor
> > > >
> > > > std::vector<int> pt({{1,2}});
> > >
> > > If you actually examine the end result the last declaration, you
> > > will discover, to your surprise, a std::vector<int> with just one
> > > value, and that would hopefully be a big honking clue as to what
> > > this is doing.
> >
> > Hm, I did that and it failed to surprise me.
> >
> >
> > #include <vector>
> > #include <string>
> > #include <iostream>
> >
> > std::vector<int> pt({{1,2}});
> > int main() {
> > for (auto p:pt) std::cout<<p<<std::endl;
> > }
> >
> > gives:
> > 1
> > 2
> >
> > here. Just as I expected.
> > > { /* something */ } introduces a braced initialization list.
> > >
> > > { { /* something */ } } introduces a braced initialization list with
> > > one value.
> >
> > But didn't we need to use two braces for container intialization?
>
> Since nobody has explained that satisfactorily yet (also see Tim's
> answer to Pavel) and I just stumbled over it again, I'd like to follow
> up. In the following situation double braces are necessary. But why?
> This kind of problem led to my original belief that for containers they
> were always needed.
>
> #include <vector>
> #include <iostream>
>
> struct Foo {
> std::vector<int> v;
> Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
> };
>
> int main() {
> Foo f{{4,5}}; //double braces needed
> for (auto r:f.v)
> std::cout<<r<<std::endl;
> return 0;
> }
>
>
> With single braces I get:
> problem.cc: In function ‘int main()’:
> problem.cc:10:14: error: no matching function for call to ‘Foo::Foo(<brace-enclosed initializer list>)’
> 10 | Foo f{4,5};
> | ^
> problem.cc:6:14: note: candidate: ‘Foo::Foo(std::vector<int>)’
> 6 | Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
> | ^~~
> problem.cc:6:14: note: candidate expects 1 argument, 2 provided
> problem.cc:4:8: note: candidate: ‘Foo::Foo(const Foo&)’
> 4 | struct Foo {
> | ^~~
> problem.cc:4:8: note: candidate expects 1 argument, 2 provided
> problem.cc:4:8: note: candidate: ‘Foo::Foo(Foo&&)’
> problem.cc:4:8: note: candidate expects 1 argument, 2 provided
I do not have all the previous exchanges available so I may be
repeating what others have said, but the reason why single braces fail
is because you are passing two arguments to a conversion constructor of
Foo taking one (or no) argument. As to why double braces work, my guess
is that (ignoring elision) the inner braces perform an implicit
conversion to std::vector<int> via std::vector's constructor taking an
initializer_list, constructing a temporary vector with two elements in
the vector, and that this then initializes Foo, using std::vector's move
constructor.
But as I say, that is a guess.
[toc] | [prev] | [next] | [standalone]
| From | Ralf Goertz <me@myprovider.invalid> |
|---|---|
| Date | 2019-11-22 16:03 +0100 |
| Message-ID | <qr8tbg$qem$1@dont-email.me> |
| In reply to | #69981 |
Am Fri, 22 Nov 2019 13:44:09 +0000
schrieb Chris Vine <chris@cvine--nospam--.freeserve.co.uk>:
> On Fri, 22 Nov 2019 12:12:43 +0100
> Ralf Goertz <me@myprovider.invalid> wrote:
> > Since nobody has explained that satisfactorily yet (also see Tim's
> > answer to Pavel) and I just stumbled over it again, I'd like to
> > follow up. In the following situation double braces are necessary.
> > But why? This kind of problem led to my original belief that for
> > containers they were always needed.
> >
> > #include <vector>
> > #include <iostream>
> >
> > struct Foo {
> > std::vector<int> v;
> > Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
> > };
> >
> > int main() {
> > Foo f{{4,5}}; //double braces needed
> > for (auto r:f.v)
> > std::cout<<r<<std::endl;
> > return 0;
> > }
> >
> >
> > With single braces I get:
> > problem.cc: In function ‘int main()’:
> > problem.cc:10:14: error: no matching function for call to
> > ‘Foo::Foo(<brace-enclosed initializer list>)’ 10 | Foo f{4,5};
> > | ^
> > problem.cc:6:14: note: candidate: ‘Foo::Foo(std::vector<int>)’
> > 6 | Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
> > | ^~~
> > problem.cc:6:14: note: candidate expects 1 argument, 2 provided
> > problem.cc:4:8: note: candidate: ‘Foo::Foo(const Foo&)’
> > 4 | struct Foo {
> > | ^~~
> > problem.cc:4:8: note: candidate expects 1 argument, 2 provided
> > problem.cc:4:8: note: candidate: ‘Foo::Foo(Foo&&)’
> > problem.cc:4:8: note: candidate expects 1 argument, 2 provided
>
> I do not have all the previous exchanges available so I may be
> repeating what others have said, but the reason why single braces fail
> is because you are passing two arguments to a conversion constructor
> of Foo taking one (or no) argument. As to why double braces work, my
> guess is that (ignoring elision) the inner braces perform an implicit
> conversion to std::vector<int> via std::vector's constructor taking an
> initializer_list, constructing a temporary vector with two elements in
> the vector, and that this then initializes Foo, using std::vector's
> move constructor.
Okay I guessed that much, but why can't the compiler do the latter when
there is only one pair of braces? The error message clearly indicates
that an initializer list is considered.
By the way my original question was why a double brace initializer list
doesn't work for
std::vector<std::string> pt({{"1","2"}});
(it compiles but throws a std::length_error because the arguments are
taken as pointers) but works perfectly for
std::vector<int> pt({{1,2}});
On the other hand
std::vector<std::string> pt({{"1","2","3"}});
again works as expected (with or without the parenthesis) which I just
figured out. So the two string element case fails only because the
compiler picks the wrong constructor. I find that odd because the
initializer list seems to be the more obvious choice…
[toc] | [prev] | [next] | [standalone]
| From | Bo Persson <bo@bo-persson.se> |
|---|---|
| Date | 2019-11-22 16:22 +0100 |
| Message-ID | <h3qcpoF446jU1@mid.individual.net> |
| In reply to | #69984 |
On 2019-11-22 at 16:03, Ralf Goertz wrote:
> Am Fri, 22 Nov 2019 13:44:09 +0000
> schrieb Chris Vine <chris@cvine--nospam--.freeserve.co.uk>:
>
>> On Fri, 22 Nov 2019 12:12:43 +0100
>> Ralf Goertz <me@myprovider.invalid> wrote:
>>> Since nobody has explained that satisfactorily yet (also see Tim's
>>> answer to Pavel) and I just stumbled over it again, I'd like to
>>> follow up. In the following situation double braces are necessary.
>>> But why? This kind of problem led to my original belief that for
>>> containers they were always needed.
>>>
>>> #include <vector>
>>> #include <iostream>
>>>
>>> struct Foo {
>>> std::vector<int> v;
>>> Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
>>> };
>>>
>>> int main() {
>>> Foo f{{4,5}}; //double braces needed
>>> for (auto r:f.v)
>>> std::cout<<r<<std::endl;
>>> return 0;
>>> }
>>>
>>>
>>> With single braces I get:
>>> problem.cc: In function ‘int main()’:
>>> problem.cc:10:14: error: no matching function for call to
>>> ‘Foo::Foo(<brace-enclosed initializer list>)’ 10 | Foo f{4,5};
>>> | ^
>>> problem.cc:6:14: note: candidate: ‘Foo::Foo(std::vector<int>)’
>>> 6 | Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
>>> | ^~~
>>> problem.cc:6:14: note: candidate expects 1 argument, 2 provided
>>> problem.cc:4:8: note: candidate: ‘Foo::Foo(const Foo&)’
>>> 4 | struct Foo {
>>> | ^~~
>>> problem.cc:4:8: note: candidate expects 1 argument, 2 provided
>>> problem.cc:4:8: note: candidate: ‘Foo::Foo(Foo&&)’
>>> problem.cc:4:8: note: candidate expects 1 argument, 2 provided
>>
>> I do not have all the previous exchanges available so I may be
>> repeating what others have said, but the reason why single braces fail
>> is because you are passing two arguments to a conversion constructor
>> of Foo taking one (or no) argument. As to why double braces work, my
>> guess is that (ignoring elision) the inner braces perform an implicit
>> conversion to std::vector<int> via std::vector's constructor taking an
>> initializer_list, constructing a temporary vector with two elements in
>> the vector, and that this then initializes Foo, using std::vector's
>> move constructor.
>
> Okay I guessed that much, but why can't the compiler do the latter when
> there is only one pair of braces? The error message clearly indicates
> that an initializer list is considered.
>
> By the way my original question was why a double brace initializer list
> doesn't work for
>
> std::vector<std::string> pt({{"1","2"}});
>
> (it compiles but throws a std::length_error because the arguments are
> taken as pointers) but works perfectly for
>
> std::vector<int> pt({{1,2}});
>
> On the other hand
>
> std::vector<std::string> pt({{"1","2","3"}});
>
> again works as expected (with or without the parenthesis) which I just
> figured out. So the two string element case fails only because the
> compiler picks the wrong constructor. I find that odd because the
> initializer list seems to be the more obvious choice…
>
It is not *that* obvious to the compiler, as it tries to match an
initializer_list<char> and you supply two char*. Unfortunately that is a
better match for std::string(iterator, iterator), but then of course
fails at runtime as it is not a valid iterator pair.
For *any* number of strings other than 2, the std::string(iterator,
iterator) will immediately be ruled out, so works much better.
You would have a better chance to match the vector's initializer_list
constructor if you actually provided som std::string literals:
std::vector<std::string> pt({{"1"s,"2"s}});
Bo Persson
[toc] | [prev] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2019-11-22 16:13 +0000 |
| Message-ID | <20191122161349.4d131fd29c07b33d87ad9f08@cvine--nospam--.freeserve.co.uk> |
| In reply to | #69984 |
On Fri, 22 Nov 2019 16:03:12 +0100
Ralf Goertz <me@myprovider.invalid> wrote:
> Am Fri, 22 Nov 2019 13:44:09 +0000
> schrieb Chris Vine <chris@cvine--nospam--.freeserve.co.uk>:
> > On Fri, 22 Nov 2019 12:12:43 +0100
> > Ralf Goertz <me@myprovider.invalid> wrote:
> > > Since nobody has explained that satisfactorily yet (also see Tim's
> > > answer to Pavel) and I just stumbled over it again, I'd like to
> > > follow up. In the following situation double braces are necessary.
> > > But why? This kind of problem led to my original belief that for
> > > containers they were always needed.
> > >
> > > #include <vector>
> > > #include <iostream>
> > >
> > > struct Foo {
> > > std::vector<int> v;
> > > Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
> > > };
> > >
> > > int main() {
> > > Foo f{{4,5}}; //double braces needed
> > > for (auto r:f.v)
> > > std::cout<<r<<std::endl;
> > > return 0;
> > > }
> > >
> > >
> > > With single braces I get:
> > > problem.cc: In function ‘int main()’:
> > > problem.cc:10:14: error: no matching function for call to
> > > ‘Foo::Foo(<brace-enclosed initializer list>)’ 10 | Foo f{4,5};
> > > | ^
> > > problem.cc:6:14: note: candidate: ‘Foo::Foo(std::vector<int>)’
> > > 6 | Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
> > > | ^~~
> > > problem.cc:6:14: note: candidate expects 1 argument, 2 provided
> > > problem.cc:4:8: note: candidate: ‘Foo::Foo(const Foo&)’
> > > 4 | struct Foo {
> > > | ^~~
> > > problem.cc:4:8: note: candidate expects 1 argument, 2 provided
> > > problem.cc:4:8: note: candidate: ‘Foo::Foo(Foo&&)’
> > > problem.cc:4:8: note: candidate expects 1 argument, 2 provided
> >
> > I do not have all the previous exchanges available so I may be
> > repeating what others have said, but the reason why single braces fail
> > is because you are passing two arguments to a conversion constructor
> > of Foo taking one (or no) argument. As to why double braces work, my
> > guess is that (ignoring elision) the inner braces perform an implicit
> > conversion to std::vector<int> via std::vector's constructor taking an
> > initializer_list, constructing a temporary vector with two elements in
> > the vector, and that this then initializes Foo, using std::vector's
> > move constructor.
>
> Okay I guessed that much, but why can't the compiler do the latter when
> there is only one pair of braces? The error message clearly indicates
> that an initializer list is considered.
That's because in the absence of an initializer-list constructor for
Foo, the compiler is obliged to treat 'Foo f{4,5}' as a call to a
constructor taking two arguments, and you haven't provided an initializer
list constructor for Foo. The only constructors available are the
user-provided constructor (which is a combined default constructor and
conversion constructor), and the compiler provided copy constructor and
move constructor. You will see that the compiler did consider these as
candidates, and then (obviously) found no match.
Leaving that aside, I think the main aim is to write intelligible code,
not code which scratches at the margins of C++'s implicit conversions.
So instead of writing 'Foo f{{4,5}}', write 'Foo f{std::vector<int>{4,5}}',
and likewise with your vector of string. Or provide an initializer-list
constructor. You will then be able to understand your intentions when
you come back to it 6 months later.
> By the way my original question was why a double brace initializer list
> doesn't work for
>
> std::vector<std::string> pt({{"1","2"}});
>
> (it compiles but throws a std::length_error because the arguments are
> taken as pointers) but works perfectly for
>
> std::vector<int> pt({{1,2}});
>
> On the other hand
>
> std::vector<std::string> pt({{"1","2","3"}});
>
> again works as expected (with or without the parenthesis) which I just
> figured out. So the two string element case fails only because the
> compiler picks the wrong constructor. I find that odd because the
> initializer list seems to be the more obvious choice…
You have received a separate answer to that.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2019-11-28 13:44 -0800 |
| Message-ID | <86h82nad6i.fsf@linuxsc.com> |
| In reply to | #69984 |
Ralf Goertz <me@myprovider.invalid> writes:
> Am Fri, 22 Nov 2019 13:44:09 +0000
> schrieb Chris Vine <chris@cvine--nospam--.freeserve.co.uk>:
>
>> On Fri, 22 Nov 2019 12:12:43 +0100
>> Ralf Goertz <me@myprovider.invalid> wrote:
>>
>>> Since nobody has explained that satisfactorily yet (also see Tim's
>>> answer to Pavel) and I just stumbled over it again, I'd like to
>>> follow up. In the following situation double braces are necessary.
>>> But why? This kind of problem led to my original belief that for
>>> containers they were always needed.
>>>
>>> #include <vector>
>>> #include <iostream>
>>>
>>> struct Foo {
>>> std::vector<int> v;
>>> Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
>>> };
>>>
>>> int main() {
>>> Foo f{{4,5}}; //double braces needed
>>> for (auto r:f.v)
>>> std::cout<<r<<std::endl;
>>> return 0;
>>> }
>>>
>>>
>>> With single braces I get:
>>> problem.cc: In function ?int main()?:
>>> problem.cc:10:14: error: no matching function for call to
>>> ?Foo::Foo(<brace-enclosed initializer list>)? 10 | Foo f{4,5};
>>> | ^
>>> problem.cc:6:14: note: candidate: ?Foo::Foo(std::vector<int>)?
>>> 6 | Foo(std::vector<int> v_=std::vector<int>()) : v(v_){}
>>> | ^~~
>>> problem.cc:6:14: note: candidate expects 1 argument, 2 provided
>>> problem.cc:4:8: note: candidate: ?Foo::Foo(const Foo&)?
>>> 4 | struct Foo {
>>> | ^~~
>>> problem.cc:4:8: note: candidate expects 1 argument, 2 provided
>>> problem.cc:4:8: note: candidate: ?Foo::Foo(Foo&&)?
>>> problem.cc:4:8: note: candidate expects 1 argument, 2 provided
>>
>> I do not have all the previous exchanges available so I may be
>> repeating what others have said, but the reason why single braces fail
>> is because you are passing two arguments to a conversion constructor
>> of Foo taking one (or no) argument. As to why double braces work, my
>> guess is that (ignoring elision) the inner braces perform an implicit
>> conversion to std::vector<int> via std::vector's constructor taking an
>> initializer_list, constructing a temporary vector with two elements in
>> the vector, and that this then initializes Foo, using std::vector's
>> move constructor.
>
> Okay I guessed that much, but why can't the compiler do the latter when
> there is only one pair of braces? The error message clearly indicates
> that an initializer list is considered.
I don't have anything to add about why this example behaves as it
does, but I have two related comments. One, it doesn't surprise
me at all that two pairs are braces are needed. Two, I'm not
sure having it work with only one pair of braces is a good idea;
if anything I suspect it would be more confusing than helpful.
My analogy is initializing structs (for C, I don't know if C++
might have changed here): I know there are rules for omitting
braces in some cases when initializing structs with nested arrays
or nested structs, but it seems insane to take advantage of those
rules. Presumably they were put in for compatibility with early
C code, which makes sense, but not continuing to use them going
forward. Returning to the C++ example, having two pairs of
braces matches my understanding of what's going on; allowing
only one pair of braces to do the same thing would mean I have
to create a more elaborate and more complicated mental model to
understand what the program is doing and how the language works.
I would much rather not to have to create these more elaborate
models. Do you see what I mean?
[toc] | [prev] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2019-11-22 16:30 +0100 |
| Message-ID | <qr8uug$57p$1@dont-email.me> |
| In reply to | #68210 |
On 04.09.2019 14:20, Sam wrote:
> Ralf Goertz writes:
>
>> Hi,
>>
>> why does the declaration
>>
>> std::vector<std::string> pt({{"One","Two"}});
>>
>> throw:
>>
>> terminate called after throwing an instance of 'std::length_error'
>> what(): basic_string::_M_create
>
> Because this is undefined behavior.
That's the short explanation, but it would be even more informative to
mention why.
Like,
#include <iostream>
#include <string>
#include <vector>
using namespace std;
auto main()
-> int
{
const auto& data = "Hello";
std::vector<std::string> v( {{&data[0], &data[4]}} );
std::cout << v.front() << "!" << endl;
}
Result:
Hell!
I.e. it uses the `string` constructor that takes two iterators.
>> whereas neither
>>
>> std::vector<std::string> pt({{"One"}});
This looks like it would use the `string` constructor that takes a
pointer to C string.
>> nor
>>
>> std::vector<int> pt({{1,2}});
>
> If you actually examine the end result the last declaration, you will
> discover, to your surprise, a std::vector<int> with just one value, and
> that would hopefully be a big honking clue as to what this is doing.
No, this one is more tricky.
Apparently it uses the move constructor of `vector` to take a temporary
`vector` constructed from the inner braces as an `initializer_list`.
Disclaimer: the results are consistent with this explanation, and there
doesn't seem to be any other explanation, but I haven't debugged.
> { /* something */ } introduces a braced initialization list.
>
> { { /* something */ } } introduces a braced initialization list with one
> value.
>
>
> { { "One", "Two" } } attempts to construct a single std::string with two
> values. The only overloaded std::string constructor this matches is the
> one that takes a beginning iterator and an ending iterator of a sequence
> that forms the contents of the constructed std::string.
>
> Since "One" and "Two" decay to two character pointers which do not
> comprise a single sequence, you just nuked yourself from high orbit,
> hence the crash.
>
>> No warning is given during compilation by gcc version 9.2.1 20190820
>
> A compiler has no obligation to warn you when you're about to shoot
> yourself in a foot. It would be nice if it did, and in many cases it
> does; but you can't rely on your compiler to keep you from shooting
> yourself in a foot.
The problem is not the compiler or programmer. The problem IMO is some
political shenanigans in the C++ standardization committee, where they
chose to sabotage Bjarne's vision of uniform initialization by making
initializer lists take precedence in overload resolution. I prefer this
explanation of academics' childish evilness, to sheer incompetence at
that level, because the explanation of incompetence is too frightening.
The political view has so far, to my knowledge, yielded accurate
predictions and postdictions.
For example, the political view of childish sabotage indicates that the
filesystem::path UTF-8 functionality /will/ be sabotaged for Windows in
C++20, along with use of `u` prefix string literals. The `u` literals
don't make much sense in pure *nix-specific code, because literals are
UTF-8 encoded there anyway, and so the change in type doesn't matter
much there. But in Windows, those unfortunate souls who have tried to
embrace the UTF-8 world via `u` literals and filesystem::path, will have
some work to do, including implementing their own UTF-16 to UTF-8
conversion for getting an UTF-8 representation of a path.
- Alf
[toc] | [prev] | [next] | [standalone]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2019-09-04 14:43 +0200 |
| Message-ID | <qkobh4$a1m$1@news.albasani.net> |
| In reply to | #68209 |
Am 04.09.2019 um 14:09 schrieb Ralf Goertz:
> Hi,
> why does the declaration
> std::vector<std::string> pt({{"One","Two"}});
To less curly brackets! Thre must be at least 42!
Or this:
vector<string> thatsright { "One", "Two" };
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c++
csiph-web