Вопрос

По какой -то причине я думал, что C ++ 0x разрешено std::initializer_list в качестве аргумента функции для функций, которые ожидают типов, которые могут быть построены из таких, например, std::vector. Анкет Но, видимо, это не работает. Это просто мой компилятор, или это никогда не будет работать? Это из -за потенциальных проблем с разрешением перегрузки?

#include <string>
#include <vector>

void function(std::vector<std::string> vec)
{
}

int main()
{
    // ok
    std::vector<std::string> vec {"hello", "world", "test"};

    // error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
    function( {"hello", "world", "test"} );
}
Это было полезно?

Решение

У GCC есть ошибка. Стандарт делает это действительным. Видеть:

Обратите внимание, что есть две стороны этого

  • Как и какая инициализация сделана в целом?
  • Как инициализация используется во время разрешения перегрузки и какая у нее стоимость?

Первый вопрос отвечает в разделе 8.5. Анкет Второй вопрос отвечает в разделе 13.3. Анкет Например, ссылочная привязка обрабатывается при 8.5.3 а также 13.3.3.1.4, в то время как инициализация списка обрабатывается в 8.5.4 а также 13.3.3.1.5.

8.5/14,16:

Инициализация, которая возникает в форме

T x = a;

а также в прохождении аргументов, возврат функции, бросание исключения (15.1), обработка исключения (15.3), а инициализация совокупного элемента (8.5.1) называется Copy-Initialization.
.
.
Семантика инициализаторов заключается в следующем [...]: если инициализатор является листом привязанного инициалиста, объект инициализируется список (8.5.4).

При рассмотрении кандидата function, компилятор увидит список инициализаторов (который еще не имеет типа - это просто грамматическая конструкция!) В качестве аргумента и std::vector<std::string> как параметр function. Анкет Выяснить, какова стоимость конверсии и есть ли мы Можно преобразовать их в контексте перегрузки, 13.3.3.1/5 говорит

13.3.3.1.5/1:

Когда аргумент является списком инициализаторов (8.5.4), это не выражение, и специальные правила применяются для преобразования его в тип параметра.

13.3.3.1.5/3:

В противном случае, если параметр представляет собой неагрегитный класс X, а разрешение перегрузки на 13.3.1.7 выбирает один лучший конструктор x для выполнения инициализации объекта типа X из списка инициализатора аргумента, последовательность неявного преобразования-это пользователь-пользователь-пользователь. Определенная последовательность преобразования. Пользовательские преобразования разрешены для преобразования элементов списка инициализаторов в типы параметров конструктора, за исключением случаев, отмеченных в 13.3.3.1.

Неагрегатный класс X является std::vector<std::string>, и я выясню один лучший конструктор ниже. Последнее правило предоставляет нам использование определенных пользовательских конверсий в таких случаях, как следующие:

struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }

Нам разрешено преобразовать струнный буквальный в std::string, даже если это требует определенного пользователя преобразования. Однако это указывает на ограничения другого абзаца. Что значит 13.3.3.1 сказать?

13.3.3.1/4, который является абзацем, ответственным за запрещение нескольких пользовательских конверсий. Мы рассмотрим только инициализации списка:

Однако при рассмотрении аргумента функции преобразования, определенной пользователем [(или конструктора)], которая является кандидатом по [...] 13.3.1.7 при прохождении списка инициализатора в виде одного аргумента или когда в списке инициализаторов есть ровно один элемент и преобразование в какой-то класс X или ссылка на (возможно, CV-квалификацию) x рассматривается для первого параметра конструктора x или [...], разрешены только стандартные последовательности преобразования и последовательности преобразования эллипсиса.

Обратите внимание, что это важное ограничение: если бы это не было для этого, вышеупомянутое может использовать конструктор копирования для установления одинаково хорошей последовательности преобразования, и инициализация была бы неоднозначной. (Обратите внимание на потенциальную путаницу «a или b и c» в этом правиле: оно должно сказать «(a или b) и c» - поэтому мы ограничены Только При попытке преобразовать конструктор x, имеющий параметр типа X).

Мы делегированы 13.3.1.7 Для сбора конструкторов мы можем использовать для этого преобразования. Давайте подходим к этому абзацу с общей стороны, начиная с 8.5 который делегировал нас 8.5.4:

8.5.4/1:

Инициализация списка может происходить в контекстах прямого инициализации или копии; инициализация списка в контексте прямого инициализации называется Прямая инициализация и инициализация списка в контексте копии инициализации называется копия-лайст-инициализация.

8.5.4/2:

Конструктор - это конструктор-списка инициализатора Если его первый параметр имеет тип std::initializer_list<E> или ссылка на возможную квалификацию CV std::initializer_list<E> Для некоторого типа E, и либо нет других параметров, либо все другие параметры имеют аргументы по умолчанию (8.3.6).

8.5.4/3:

Инициализация списка объекта или ссылка типа T определяется следующим образом: [...] в противном случае, если T-тип класса, рассматриваются конструкторы. Если T имеет конструктор-лист инициализатора, список аргументов состоит из списка инициализатора как единый аргумент; В противном случае список аргументов состоит из элементов списка инициализаторов. Применимые конструкторы перечислены (13.3.1.7), и лучший из них выбирается посредством разрешения перегрузки (13.3).

На данный момент, T это тип класса std::vector<std::string>. Анкет У нас есть один аргумент (который еще не имеет типа! Мы просто находимся в контексте грамматического списка инициализатора). Конструкторы перечисляются 13.3.1.7:

...] Если T имеет конструктор-списка инициализатора (8.5.4), список аргументов состоит из списка инициализатора как единый аргумент; В противном случае список аргументов состоит из элементов списка инициализаторов. Для инициализации копий-списка функции кандидатов являются все конструкторы T. Однако, если выбран явный конструктор, инициализация плохо образуется.

Мы рассмотрим только список инициализаторов std::vector Как единственный кандидат, поскольку мы уже знаем, что другие не победит против этого или не подходят для аргумента. У него следующая подпись:

vector(initializer_list<std::string>, const Allocator& = Allocator());

Теперь правила преобразования списка инициализаторов в std::initializer_list<T> (Чтобы классифицировать стоимость преобразования аргумента/параметра) перечислены в 13.3.3.1.5:

Когда аргумент является списком инициализаторов (8.5.4), это не выражение, и специальные правила применяются для преобразования его в тип параметра. [...] Если тип параметра std::initializer_list<X> и все элементы списка инициализатора могут быть неявно преобразованы в X, неявная последовательность преобразования является худшим преобразованием, необходимым для преобразования элемента списка в X. Это преобразование может быть конверсией пользователя Даже в контексте призывов к конструктору-списку инициализатора.

Теперь список инициализаторов будет успешно преобразован, а последовательность преобразования - это пользовательский преобразование (от char const[N] к std::string) Как это сделано 8.5.4 опять таки:

В противном случае, если t является специализацией std::initializer_list<E>, Объект инициализатора_листа построен, как описано ниже, и используется для инициализации объекта в соответствии с правилами инициализации объекта из класса того же типа (8.5). (...)

Видеть 8.5.4/4 Как сделан этот последний шаг :)

Другие советы

Кажется, это работает так:

function( {std::string("hello"), std::string("world"), std::string("test")} );

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

Offhand, я не уверен, но я подозреваю, что здесь происходит то, что конвертирование в initializer_list - это одно преобразование, и преобразование его в вектор - это еще одно преобразование. Если это так, вы превышаете предел только одного неявного преобразования ...

Это либо ошибка компилятора, либо ваш компилятор не поддерживает std :: initializer_list. Протестировано на GCC 4.5.1, и он собирает нормально.

Вы должны указать тип вашего инициализатора

function(std::initializer_list<std::string>{"hello", "world", "test"} );

Удачи

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top