Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #68209 > unrolled thread

Initialization of std::vector<std::string> throws

Started byRalf Goertz <me@myprovider.invalid>
First post2019-09-04 14:09 +0200
Last post2019-09-04 14:43 +0200
Articles 13 — 8 participants

Back to article view | Back to comp.lang.c++


Contents

  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

#68209 — Initialization of std::vector<std::string> throws

FromRalf Goertz <me@myprovider.invalid>
Date2019-09-04 14:09 +0200
SubjectInitialization 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]


#68210

FromSam <sam@email-scan.com>
Date2019-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]


#68211

FromRalf Goertz <me@myprovider.invalid>
Date2019-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]


#68247

FromPavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
Date2019-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]


#68505

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-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]


#69976

FromRalf Goertz <me@myprovider.invalid>
Date2019-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]


#69981

FromChris Vine <chris@cvine--nospam--.freeserve.co.uk>
Date2019-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]


#69984

FromRalf Goertz <me@myprovider.invalid>
Date2019-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]


#69986

FromBo Persson <bo@bo-persson.se>
Date2019-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]


#69995

FromChris Vine <chris@cvine--nospam--.freeserve.co.uk>
Date2019-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]


#70095

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-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]


#69988

From"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date2019-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]


#68212

FromBonita Montero <Bonita.Montero@gmail.com>
Date2019-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