22

clang and gcc differ in behaviour for the following code:

struct foo
{
    foo(int);
};

struct waldo
{
    template <typename T>
    operator T();
};

int main()
{
    waldo w;
    foo f{w};
}

This code is accepted by clang, with the foo(int) constructor being called. However, gcc complains about an ambiguity between the foo(int) constructor and the implicitly generated copy and move constructors:

test.cpp: In function 'int main()':
test.cpp:15:12: error: call of overloaded 'foo(<brace-enclosed initializer list>)' is ambiguous
     foo f{w};
            ^
test.cpp:15:12: note: candidates are:
test.cpp:3:5: note: foo::foo(int)
     foo(int);
     ^
test.cpp:1:8: note: constexpr foo::foo(const foo&)
 struct foo
        ^
test.cpp:1:8: note: constexpr foo::foo(foo&&)

Who is right?

It's also interesting to note that if foo f{w} is changed to foo f(w) (note the change from braces to parentheses), both gcc and clang give an error. This makes me hope that gcc's behaviour for the above example (i.e. giving an error) is correct, otherwise there would be a strange inconsistency between the () and {} forms of initialization.

EDIT: Following Kerrek SB's suggestion, I tried deleteing the copy constructor of foo:

struct foo
{
    foo(int);
    foo(const foo&) = delete;
};

The behaviour remains the same.

HighCommander4
  • 50,428
  • 24
  • 122
  • 194
  • 1
    I'd go with GCC on this one: Both conversions to `int` and to `Foo` are possible, so this really looks ambiguous. Try deleting or defaulting the copy/move constructors, though, and compare. – Kerrek SB Apr 17 '13 at 07:51
  • 4
    @KerrekSB deleted functions do participate in overload resolution. – TemplateRex Apr 17 '13 at 08:01
  • which versions of clang/gcc did you test? – TemplateRex Apr 17 '13 at 08:26
  • 4
    Apparently (according to slide 17 of [this talk](http://accu.org/content/conf2013/Bjarnes_Talk.pdf)) that style of brace-initialisation can't be used for copying in C++11, which would make Clang correct. It's regarded as a defect, probably to be fixed in C++14, making GCC correct. However, I can't seem to find the wording in C++11 that prevents it, so I can't give a definitive answer. – Mike Seymour Apr 17 '13 at 08:28
  • @MikeSeymour: I think you should go ahead and post it as an answer, people can always chime in with the legalese later. – Matthieu M. Apr 17 '13 at 09:14
  • @MatthieuM.: No need, someone's already found the relevant standardese. – Mike Seymour Apr 17 '13 at 09:26
  • 1
    @MikeSeymour: litb, of course :p And thanks for Bjarne's talk by the way, I had not seen it! – Matthieu M. Apr 17 '13 at 09:32
  • @mat at a rouhg skim through slide 17, that slide appears to be about something else (perhaps a ljbrary problem about std::complex??). Copying class T to class T (as long as they are notaggregates) is fine because there will be no user defined cconversion sequence. It is definitely not the issue in this question. – Johannes Schaub - litb Apr 17 '13 at 21:33

1 Answers1

11

For list initialization, if the element of the list has one element (here, w), and a constructor of a class X with parameter "reference to const/volatile X" is considered, no user defined conversions are considered. So both the copy and move constructor of foo cannot be used. So the foo(int) constructor is unambiguously chosen.

So Clang is correct here.

EDIT: For the Standards folks in here, see 13.3.3.1p4

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212