C++ Copy constructor gets called instead of initializer_list<> -
based on code
struct foo { foo() { cout << "default ctor" << endl; } foo(std::initializer_list<foo> ilist) { cout << "initializer list" << endl; } foo(const foo& copy) { cout << "copy ctor" << endl; } }; int main() { foo a; foo b(a); // calls copy constructor again! //shouldn't call initializer_list constructor? foo c{b}; _getch(); return 0; }
the output is:
default ctor
copy ctor
copy ctor
in third case, i'm putting b brace-initialization should call initializer_list<> constructor.
instead, copy constructor takes lead.
will of tell me how works , why?
as pointed out nicol bolas, original version of answer incorrect: cppreference @ time of writing incorrectly documented order in constructors considered in list-initialization. below answer using rules exist in n4140 draft of standard, close official c++14 standard.
the text of original answer still included, record.
updated answer
per nathanoliver's comment, gcc , clang produce different outputs in situation:
g++ -std=c++14 -wall -pedantic -pthread main.cpp && ./a.out default ctor copy ctor copy ctor initializer list clang++ -std=c++14 -wall -pedantic -pthread main.cpp && ./a.out default ctor copy ctor copy ctor
gcc correct.
n4140 [dcl.init.list]/1
list-initialization initialization of object or reference braced-init-list.
you're using list-initialization there, , since c
object, rules list-initialization defined in [dcl.init.list]/3:
[dcl.init.list]/3:
list-initialization of object or reference of type t defined follows:
- if
t
aggregate...- otherwise, if initializer list has no elements...
- otherwise, if
t
specialization ofstd::initializer_list<e>
...
going through list far:
foo
not aggregate.- it has 1 element.
foo
not specialization ofstd::initializer_list<e>
.
then hit [dcl.init.list]/3.4:
otherwise, if
t
class type, constructors considered. applicable constructors enumerated , best 1 chosen through overload resolution (13.3, 13.3.1.7). if narrowing conversion (see below) required convert of arguments, program ill-formed.
now we're getting somewhere. 13.3.1.7 known [over.match.list]:
initialization list-initialization
when objects of non-aggregate class typet
list-initialized (8.5.4), overload resolution selects constructor in 2 phases:
- initially, candidate functions initializer-list constructors (8.5.4) of class
t
, argument list consists of initializer list single argument.- if no viable initializer-list constructor found, overload resolution performed again, candidate functions constructors of class
t
, argument list consists of elements of initializer list.
so copy constructor considered after initializer list constructors, in second phase of overload resolution. initializer list constructor should used here.
it's worth noting [over.match.list] continues with:
if initializer list has no elements ,
t
has default constructor, first phase omitted. in copy-list initialization, if explicit constructor chosen, initialization ill-formed.
and after [dcl.init.list]/3.5 deals single-element list initialization:
otherwise, if initializer list has single element of type
e
, eithert
not reference type or referenced type reference-relatede
, object or reference initialized element; if narrowing conversion (see below) required convert elementt
, program ill-formed.
which explains cppreference got special case single-element list initialization, though placed higher in order should be.
original answer
you're encountering interesting aspect of list initialization, if list fulfills requirements may treated copy-initialization rather list-initialization.
from cppreference:
the effects of list initialization of object of type
t
are:if
t
class type , initializer list has single element of same or derived type (possibly cv-qualified), object initialized element (by copy-initialization copy-list-initialization, or direct-initialization direct-list-initialization). (since c++14)
foo c{b}
fulfills these requirements.
Comments
Post a Comment