Может ли конструктор диапазона std :: vector вызывать явные преобразования?

14

Правильно ли сформирована следующая программа?

#include <vector>
struct A {
    explicit A(int) {}
};
int main() {
    std::vector<int> vi = {1, 2, 3, 4, 5};
    std::vector<A> va(vi.begin(), vi.end());
}

Согласно C ++ 17 [sequence.reqmts], требование для

X u(i, j);

где Xконтейнер последовательности, это:

Tдолжен быть EmplaceConstructibleв Xот *i.

Однако в предыдущем абзаце указано, что:

iи jобозначают итераторы, удовлетворяющие требованиям входных итераторов, и ссылаются на элементы, неявно преобразуемые в value_type,

Таким образом, мне кажется, что оба требования должны быть выполнены: тип значения диапазона должен быть неявно конвертируемым в тип значения контейнера и EmplaceConstructible должен быть удовлетворен (что означает, что распределитель должен быть в состоянии выполнить требуемую инициализацию) , Поскольку эта программа не может быть intпреобразована в неявно A, она должна быть некорректной.

Однако, что удивительно, похоже, что компилируется в GCC .

Брайан
источник
(Для справки , это не только gcc: godbolt.org/z/ULeRDw )
Макс Лангхоф
В этом случае неявное преобразование не требуется, поскольку явный конструктор уже соответствует типу. Я думаю, что описание сбивает с толку, но явное построение всегда лучше, чем неявное преобразование перед построением.
JHBonarius

Ответы:

2

Для контейнеров последовательностей требуется только поддержка конструкции из итераторов, которые удовлетворяют критериям неявной конвертируемости.

Это само по себе не запрещает контейнерам последовательности поддерживать эту конструкцию от итераторов, которые не соответствуют этим критериям, насколько я могу судить 1 . Существует явное правило об этом:

Если конструктор ... вызывается с типом InputIterator, который не квалифицируется как входной итератор , тогда конструктор не должен участвовать в разрешении перегрузки.

Неясно, что именно «квалифицировать как входной итератор» означает именно в контексте. Это неофициальный способ выражения Cpp17InputIterator, или он пытается сослаться на требования i и j? Я не знаю. Независимо от того, разрешено это или нет, стандарт не имеет строгих требований для его обнаружения:

[Container.requirements.general]

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

С интерпретацией, что любой Cpp17InputIterator «квалифицируется как входной итератор», примерная программа не должна быть плохо сформированной. Но это также не гарантирует, что он будет хорошо сформирован.

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


PS Это компилируется без предупреждений в Clang (с libc ++) и Msvc.

PPS Эта формулировка, кажется, была добавлена ​​в C ++ 11 (что естественно, так как тогда были также введены явные конструкторы).

eerorika
источник
1
Действительно зависит от того, что означает «не квалифицируется как входной итератор» . В отличие от выше , он на самом деле не говорит Cpp17InputIterator, поэтому мне не ясно, включен ли «и относится к элементам, неявно преобразуемым в value_type», во «входной итератор». Если это так, то конструктор не должен участвовать в разрешении перегрузки, и программа должна быть некорректной.
Макс
1
Итак, каждому стандартному библиотечному классу разрешено иметь дополнительные конструкторы без выдачи диагностики, когда эти дополнительные конструкторы используются? Это интуитивно кажется мне неправильным ...
Брайан,
@ Брайан Я не уверен, что это «дополнительные конструкторы», но, может быть, более «конкретные реализации конструкторов, которые позволяют больше места». Проверка каждого ввода может оказать существенное влияние на производительность, поэтому я не знаю, будет ли это правильным способом ...
JHBonarius
@ Брайан Это, конечно, было бы плохой идеей, даже если явно не запрещено. В этом случае мы только рассматриваем, разрешено ли требуемому конструктору поддерживать типы итераторов, которые он не обязан поддерживать. В этом случае существует явное требование «не участвовать», как указал Макс, что, безусловно, запретило бы это. Но на самом деле неясно, что именно «квалифицировать как входной итератор» означает именно в контексте. Это неформальный способ выразить Cpp17InputIterator, или он пытается сослаться на требования iи j? Я не знаю.
eerorika
2
1) В C ++ мы устанавливаем низкий стандарт. , 2) Конструкторы являются не виртуальными функциями-членами . 3) См. LWG 3297 ; однако я не особенно убежден, что мы должны удалить неявное требование преобразования.
ТК