c++ - C++11 constructor overload resolution and initialiser_lists: clang++ and g++ disagree -


i have small piece of c++11 code g++ (4.7 or 4.8) refuses compile claiming call constructor b2 b2a(x, {p(y)}) ambiguous. clang++ happy code, refuses compile b2 b2b(x, {{p(y)}}) g++ happy compile!

both compilers happy b1 constructor either {...} or {{...}} argument. can c++ language lawyer explain compiler correct (if either) , going on? code below:

#include <initializer_list>  using namespace std;  class y {}; class x;  template<class t> class p { public:     p(t); };  template<class t> class { public:     a(initializer_list<t>); };  class b1 { public:     b1(const x&, const y &);     b1(const x&, const a<y> &); };  class b2 { public:     b2(const x &, const p<y> &);     b2(const x &, const a<p<y>> &); };  int f(const x &x, const y y) {     b1 b1a(x, {y});     b1 b1b(x, {{y}});     b2 b2a(x, {p<y>(y)});     b2 b2b(x, {{p<y>(y)}});     return 0; } 

and compiler errors, clang:

$ clang++ -stdlib=libc++ -std=c++11 test-initialiser-list-4.cc -o test.o -c  test-initialiser-list-4.cc:32:6: error: call constructor of 'b2' ambiguous   b2 b2(x, {{p<y>(y)}});      ^  ~~~~~~~~~~~~~~ test-initialiser-list-4.cc:26:5: note: candidate constructor     b2(const x &, const p<y> &);     ^ test-initialiser-list-4.cc:27:5: note: candidate constructor     b2(const x &, const a<p<y>> &);     ^ 

g++:

test-initialiser-list-4.cc: in function 'int f(const x&, y)': test-initialiser-list-4.cc:32:21: error: call of overloaded 'b2(const x&, <brace-enclosed initializer list>)' ambiguous    b2 b2(x, {p<y>(y)});                      ^ test-initialiser-list-4.cc:32:21: note: candidates are: test-initialiser-list-4.cc:27:5: note: b2::b2(const x&, const a<p<y> >&)      b2(const x &, const a<p<y>> &);      ^ test-initialiser-list-4.cc:26:5: note: b2::b2(const x&, const p<y>&)      b2(const x &, const p<y> &);      ^ 

this smells interaction between uniform initialisation, initialiser list syntax , function overloading templated arguments (which know g++ stringent about), i'm not enough of standards lawyer able unpack should correct behaviour here!

first code, think should happen. (in follows, ignore first parameter, since interested second parameter. first 1 exact match in example). please note rules in flux in spec, wouldn't 1 or other compiler has bug.

b1 b1a(x, {y}); 

this code cannot call const y& constructor in c++11, because y aggregate , y has no data member of type y (of course) or else initializable (this ugly, , worked on fixed - c++14 cd doesn't have wording yet, not sure whether final c++14 contain fix).

the constructor const a<y>& parameter can called - {y} taken argument constructor of a<y>, , initialize constructor's std::initializer_list<y>.

hence - second constructor called successfully.

b1 b1b(x, {{y}}); 

here, same argument counts counts constructor const y& parameter.

for constructor parameter type const a<y>&, bit more complicated. rule conversion cost in overload resolution computing cost of initializing std::initializer_list<t> requires every element of braced list convertible t. before said {y} cannot converted y (as aggregate). important know whether std::initializer_list<t> aggregate or not. frankly, have no idea whether or not must considered aggregate according standard library clauses.

if take non-aggregate, considering copy constructor of std::initializer_list<y>, again trigger exact same sequence of tests (leading "infinite recursion" in overload resolution checking). since rather weird , non-implementable, don't think implementation takes path.

if take std::initializer_list aggregate, saying "nope, no conversion found" (see above aggregates-issue). in case since cannot call initializer constructor single initializer list whole, {{y}} split multiple arguments, , constructor(s) of a<y> taking each of separately. hence, in case, end {y} initializing std::initializer_list<y> single parameter - fine , work charm.

so under assumption std::initializer_list<t> aggregate, fine , call second constructor successfully.

b2 b2a(x, {p<y>(y)}); 

in case , next case, don't have aggregate issue above y anymore, since p<y> has user-provided constructor.

for p<y> parameter constructor, parameter initialized {p<y> object}. p<y> has no initializer lists, list split individual arguments , call move-constructor of p<y> rvalue object of p<y>.

for a<p<y>> parameter constructor, same above case a<y> initialized {y}: since std::initializer_list<p<y>> can initialized {p<y> object}, argument list not split, , hence braces used initializer constructor's std::initializer_list<t>.

now, both constructors work fine. acting overloaded functions here, , second parameter in both cases requires user defined conversion. user defined conversion sequences can compared if in both cases same conversion function or constructor used - not case here. hence, ambiguous in c++11 (and in c++14 cd).

note here have subtle point explore

struct x { operator int(); x(){/*nonaggregate*/} };  void f(x); void f(int);  int main() {   x x;   f({x}); // ambiguity!   f(x); // ok, calls first f } 

this counter intuitive result fixed in same run fixing aggregate-initialization weirdness mentioned above (both call first f). implemented saying {x}->x becomes identity conversion (as x->x). currently, user-defined conversion.

so, ambiguity here.

b2 b2b(x, {{p<y>(y)}}); 

for constructor parameter const p<y>&, again split arguments , {p<y> object} argument passed constructor(s) of p<y>. remember p<y> has copy constructor. complication here not allowed use (see 13.3.3.1p4), because require user defined conversion. constructor left 1 taking y, y cannot initialized {p<y> object}.

for constructor parameter a<p<y>>, {{p<y> object}} can initialize std::initializer_list<p<y>>, because {p<y> object} convertible p<y> (other y above - dang, aggregates).

so, second constructor called successfully.


summary 4

  • second constructor called successfully
  • under assumption std::initializer_list<t> aggregate, fine , call second constructor successfully
  • ambiguity here
  • second constructor called successfully

Comments

Popular posts from this blog

php - Calling a template part from a post -

Firefox SVG shape not printing when it has stroke -

How to mention the localhost in android -