Почему многоаргументные конструкторы вектора, принимающие параметры конструкции, не помечены как "явные"?
Я заметил следующие векторные конструкторы в стандартной библиотеке C++
explicit vector(size_type n);
vector(size_type n, const T& value, const Allocator& = Allocator());
Есть ли причина, по которой второй конструктор не помечен explicit
? Это компилирует и заставляет меня чувствовать себя плохо
void f(vector<string>);
int main() {
f({10, "foo"});
}
Хотя если я опущу "foo"
, он не компилируется, и это то, что я ожидаю, когда передаю парное (составное) значение типа int и строку в функцию, которая хочет вектор строк.
3 ответа
Мне интересно, законно ли в первую очередь ожидать, что { ... }
всегда представляет список элементов контейнера при создании временного. Это, кажется, ваше предположение. IMO конструктор с одним аргументом должен быть объявлен как explicit
чтобы избежать нежелательных последовательностей преобразования или бессмысленных назначений, таких как:
vector<int> x = 3;
С другой стороны, для версии с двумя аргументами этот конструктор можно вызывать только при создании временного объекта с использованием фигурных скобок, и программисту хорошо известно, что он там вводит. Например, мне совершенно ясно, что 10
а также "hello"
не предназначены для представления списка элементов контейнера, потому что 10
это не строка
Если бы я действительно хотел передать вектор из 10 элементов, инициализированных в "hello"
, Я был бы обеспокоен необходимостью писать f(vector(10, "hello"))
вместо того, чтобы просто делать f({10, "hello"})
,
Итак, чтобы подвести итог: в то время как конструктор с одним аргументом должен быть объявлен как explicit
Я считаю, что это не обязательно для значения с двумя аргументами, потому что не все, что находится внутри пары фигурных скобок, следует интерпретировать как список элементов контейнера.
Хотя, если я опускаю "foo", он не компилируется, и это то, что я ожидаю, когда передаю парное (составное) значение типа int и строки в функцию, которая хочет вектор строк.
Нет, вы не передаете пару int и строку, но создаете вектор размером 10 с содержимым строк, например "foo". В этом нет ничего плохого! Я могу представить себе ситуацию, когда было бы полезно создать вектор, содержащий одинаковые строки с самого начала.
это то, что я ожидаю, когда я передаю парное (составное) значение типа int и строку функции, которая хочет вектор строк.
Ну, вот твоя проблема.
{...}
не является "составным значением". Это не список. Он говорит: "инициализировать объект, используя эти значения". Если рассматриваемый объект является агрегатом, он будет использовать инициализацию агрегата. Если рассматриваемый объект является неагрегированным типом, он выберет конструктор для вызова, основываясь на соответствующих конструкторах типа и различных правилах для braced-init-lists в C++11.
Вы не должны думать о {10, "foo"}
как список двух значений. Это инициализатор, который содержит два значения. Это может быть использовано с std::pair<int, const char *>
, и так далее.
Причина по которой std::vector
Конструктор не является явным, чтобы разрешить эту конструкцию. Конструктор с одним аргументом является явным, потому что в противном случае неявные правила преобразования позволили бы это:
std::vector<T> v = 5; //???
Или, что более важно:
void Foo(const std::vector<T> &v);
Foo(5); //???
Мы не хотим, чтобы целые числа были неявно преобразованы в std::vector
s. Однако, когда вы используете инициализатор, более разумно разрешить более широкий диапазон "неявных" преобразований, потому что вы можете видеть {}
синтаксис есть.
В случае с одним аргументом неясно, что означает пользователь. С синтаксисом {} понятно, что имеет в виду пользователь: инициализировать объект.
Foo({10, "foo"}); //Initializes the first argument given the values.