Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is no compile-time error when calling an ambiguous ctor?

#include <iostream>
#include <vector>

int main()
{
    auto v1 = std::vector<std::size_t>(std::size_t{8});
    std::cout << v1.size() << std::endl;

    auto v2 = std::vector<std::size_t>{std::size_t{8}};
    std::cout << v2.size() << std::endl;
}

The code outputs:

8
1

I know this is a well-known problem in C++ because of:

std::vector<std::size_t>(std::size_t{8}) calls

explicit vector(size_type count) while

std::vector<std::size_t>{std::size_t{8}} calls

vector(std::initializer_list<T> init, const Allocator& alloc = Allocator()).

To my surprise:

Why does the second call not trigger a compile-time error for overload resolution ambiguity?

In another related question, a piece of similar code does trigger an ambiguity error.

like image 229
xmllmx Avatar asked Aug 27 '19 05:08

xmllmx


1 Answers

Because there is no ambiguity that can cause an error. Overload resolution is explicitly different when we use list initialization. It's done intentionally in two phases.

[over.match.list]

1 When objects of non-aggregate class type T are list-initialized ([dcl.init.list]), overload resolution selects the constructor in two phases:

  • Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.

  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations ([over.match.ctor], [over.match.copy]), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. — end note ]

In the first step only std::initializer_list constructors are considered. And we can reach the second step where other constructors are considered only if the first overload resolution failed. Obviously overload resolution does not fail to find an appropriate std::initializer_list constructor.

The question you link to is not about ambiguity in initializing vectors. The ambiguity is in choosing a function overload. Both are viable because both accept a different vector that can be itself initialized unambiguously from the same initializer list.

like image 133
StoryTeller - Unslander Monica Avatar answered Nov 15 '22 08:11

StoryTeller - Unslander Monica