Ответ 1
Этот был тонкий.
std::vector
имеет конструктор, содержащий два итератора диапазона. Это конструктор шаблон (определенный в 23.6.6.2 стандарта С++ 11):
template<typename InputIterator>
vector(InputIterator first, InputIterator last,
const allocator_type& a = allocator_type());
Теперь конструктор std::vector<wstring>
, принимающий initializer_list
, не является совпадением для неявного преобразования в вызове функции, (const char*
и string
- разные типы); но выше, который, конечно, включен как в std::vector<string>
, так и в std::vector<wstring>
, является потенциально идеальным совпадением, потому что InputIterator
можно вывести как const char*
. Если какой-либо метод SFINAE не используется для проверки того, действительно ли аргумент выведенного шаблона действительно удовлетворяет концепции InputIterator
для векторного базового типа, что не является нашим случаем, этот конструктор жизнеспособен.
Но опять же, как std::vector<string>
, так и std::vector<wstring>
имеют жизнеспособный конструктор, который реализует преобразование из скопированного списка инициализаторов: следовательно, двусмысленность.
Таким образом, проблема заключается в том, что хотя "apple"
и "banana"
на самом деле не являются итераторами (*), они в конечном итоге рассматриваются как таковые. Добавление одного аргумента "joe"
к вызову функции устраняет проблему путем устранения неоднозначности вызова, поскольку это вынуждает компилятор исключать конструкторы на основе диапазона и выбирать только жизнеспособное преобразование (initializer_list<wstring>
не является жизнеспособный, потому что const char*
не может быть преобразован в wstring
).
* На самом деле, они являются указателями на const char
, поэтому их можно даже рассматривать как постоянные итераторы для символов, но определенно не для строк, как может думать наш конструктор шаблонов.