Почему {} в качестве аргумента функции не приводит к двусмысленности? - PullRequest
20 голосов
/ 03 марта 2020

Рассмотрим этот код:

#include <vector>
#include <iostream>

enum class A
{
  X, Y
};

struct Test
{
  Test(const std::vector<double>&, const std::vector<int>& = {}, A = A::X)
  { std::cout << "vector overload" << std::endl; }

  Test(const std::vector<double>&, int, A = A::X)
  { std::cout << "int overload" << std::endl; }
};

int main()
{
  std::vector<double> v;
  Test t1(v);
  Test t2(v, {}, A::X);
}

https://godbolt.org/z/Gc_w8i

Это печатает:

vector overload
int overload

Почему это не приводит к компиляции ошибка из-за неоднозначного разрешения перегрузки? Если второй конструктор будет удален, мы получим vector overload два раза. Как / чем метри c является int однозначно лучшим соответствием для {}, чем std::vector<int>?

Подпись конструктора, безусловно, может быть обрезана дальше, но меня только обманул эквивалентный кусок кода и хочу убедиться, что ничего важного не потеряно для этого вопроса.

1 Ответ

12 голосов
/ 03 марта 2020

Это в [over.ics.list] , выделено мое

6 В противном случае, если параметр является неагрегированным классом X и Разрешение перегрузки для [over.match.list] выбирает один лучший конструктор C из X для выполнения инициализации объекта типа X из списка инициализатора аргумента:

  • If C - это , не являющийся конструктором списка инициализаторов, и список инициализаторов имеет один элемент типа cv U, где U - X или класс, производный от X, последовательность неявного преобразования имеет ранг Точного совпадения, если U - X или ранг преобразования, если U получено из X.

  • В противном случае последовательность неявного преобразования представляет собой определяемую пользователем последовательность преобразования со второй стандартной последовательностью преобразования - преобразование идентичности.

9 В противном случае, если тип параметра не является классом:

  • [...]

  • если в списке инициализатора нет элементов, подразумевается последовательность преобразования cit - конверсия идентичности. [Пример:

    void f(int);
    f( { } ); // OK: identity conversion
    

    конец примера]

std::vector инициализируется конструктором, а жирным шрифтом отмечается, что это пользовательское преобразование. Между тем, для int это преобразование идентичности, поэтому оно превосходит звание первого c 'tor.

...