Bug 113884 - GCC rejects valid program saying ambiguous call when using std::vector
Summary: GCC rejects valid program saying ambiguous call when using std::vector
Status: RESOLVED DUPLICATE of bug 60027
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-02-12 04:51 UTC by Jason Liam
Modified: 2024-02-12 07:58 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jason Liam 2024-02-12 04:51:23 UTC
The following valid program is rejected by gcc
```
#include <type_traits>
#include <vector>

struct A {
    A();
};


void func(std::vector<double> values);
void func(std::vector<A> as);

int main() {
    func({ 4.2 }); //gcc rejects this
}
```

One way to see that gcc is incorrect here is by just removing the first overload and then gcc also starts rejecting this, implying the the second overload is not even viable so how can it make the call ambiguous.

```
#include <type_traits>
#include <vector>

struct A {
    A();
};


void func(std::vector<A> as);

int main() {
    func({ 4.2 });//now gcc also starts correctly rejecting this implying that this is not even viable so how can it possibly make the call ambiguous 
}
```
Comment 1 Andrew Pinski 2024-02-12 04:58:25 UTC
Reduced to:
```
struct B
{
  B(double);
};

struct C
{
  C(int);
};


void func(B);
void func(C);

int main() {
    func({ 4.2 });
}
```
Comment 2 Andrew Pinski 2024-02-12 05:01:30 UTC
(In reply to Andrew Pinski from comment #1)
> Reduced to:
> ```
> struct B
> {
>   B(double);
> };
> 
> struct C
> {
>   C(int);
> };
> 
> 
> void func(B);
> void func(C);
> 
> int main() {
>     func({ 4.2 });
> }
> ```

Actually C constructor should be:
  explicit  C(int);
Comment 3 Andrew Pinski 2024-02-12 05:03:22 UTC
Since explicit is needed, then this is a dup of bug 60027 which means GCC is correct at rejecting this as being ambigous.  Clang does not implement https://wg21.link/cwg1228 resolution.

*** This bug has been marked as a duplicate of bug 60027 ***
Comment 4 Jason Liam 2024-02-12 05:18:16 UTC
But which constructor is explicit here? I don't see any explicit ctor here.
Comment 5 Andrew Pinski 2024-02-12 05:22:26 UTC
(In reply to Jason Liam from comment #4)
> But which constructor is explicit here? I don't see any explicit ctor here.

std::vector 's constructor which takes std::size_t is marked as explicit.
Comment 6 Andrew Pinski 2024-02-12 05:25:26 UTC
I know cppreference is not the standard but it is a decent reference to start with.

https://en.cppreference.com/w/cpp/container/vector/vector


Without the explicit, clang will also reject it as being ambiguous. As I mentioned this is not a bug in gcc but rather a bug in clang.
Comment 7 Jason Liam 2024-02-12 05:47:10 UTC
(In reply to Andrew Pinski from comment #5)
> std::vector 's constructor which takes std::size_t is marked as explicit.

But you're missing that the initializer list ctor is preferred/choosen over the size_t arg ctor. 

See https://stackoverflow.com/questions/27144054/why-is-the-stdinitializer-list-constructor-preferred-when-using-a-braced-initi

I've created the following demo to show this:

```

class Foo
{
    //  val_;
public:
    Foo(std::initializer_list<double> il)
    {
        std::cout << "initializer_list ctor" << std::endl;
    }
     explicit  Foo(std::size_t val)
    {
        std::cout << "ctor" << std::endl;
    };
};

int main()
{
    Foo f = {1.1}; //all compiler use initializer list
}
```
Comment 8 Andrew Pinski 2024-02-12 05:59:32 UTC
(In reply to Jason Liam from comment #7)
> (In reply to Andrew Pinski from comment #5)
> > std::vector 's constructor which takes std::size_t is marked as explicit.
> 
> But you're missing that the initializer list ctor is preferred/choosen over
> the size_t arg ctor. 

That is different from your original example.
This is closer to your original example:
```
#include <initializer_list>

struct B
{
  B(std::initializer_list<double>);
};

struct C
{
  explicit C(int);
};


void func(B);
void func(C);

int main() {
    func({ 4.2 });
}
```

here we have two calls to func, one which takes B and the other which takes C. In the case case of copy-list-initialization, it is ambigous which is to be constructed as copy-list-initialization for overload resolution; explicit constructors are looked at but only an error if it was chosen. Note narrowing is not taken into account for overload resolution too; maybe that is what you are missing.

Again read https://wg21.link/cwg1228 which talks about this not being a defect in the C++ standard; this is how the standard is written.

Anyways clang's bug report is https://github.com/llvm/llvm-project/issues/28016 .

>implying the the second overload is not even viable so how can it make the call ambiguous.

I should note that is not have overload resolution works either.
Comment 9 Jason Liam 2024-02-12 07:58:43 UTC
(In reply to Andrew Pinski from comment #8)
Does that imply that following program is also invalid? GCC rejects the below program but msvc accepts.

```
struct A
{
  explicit A(int = 10);
  A()= default;
};

A a = {}; //gcc rejects this but msvc accepts
```