Разрешено ли преобразование с помощью конструктора шаблонов std :: vector с использованием итераторов? - PullRequest
17 голосов
/ 09 февраля 2012

В стандарте C ++ 11, раздел 23.3.6.2 [vector.cons], сказано следующее:

   template <class InputIterator>
     vector(InputIterator first, InputIterator last,
            const Allocator& = Allocator());

9 Эффекты : создает векторравен диапазону [first,last), используя указанный распределитель.
10 Сложность : делает только N вызовов конструктора копирования T (где N - расстояние между first и last)и никакие перераспределения, если итераторы первый и последний относятся к категориям прямого, двунаправленного или произвольного доступа.Он делает вызовы порядка N для конструктора копирования из T и перераспределения log (N), если они являются просто входными итераторами.

(этот текст также существует в более старом стандарте)С одной стороны, не требуется, чтобы разыменование InputIterator приводило к значению того же типа, которое хранится в векторе.С другой стороны, он говорит об использовании конструкторов копирования, что подразумевает один и тот же тип.

Мой вопрос : допустимо ли использовать последовательность элементов другого типа с этимконструктор, при условии, что преобразование между типами возможно?Ссылки на стандарт желательны.

Например, следующий код прекрасно работает на ideone .Это гарантировано стандартом, или только GCC позволяет это сделать?

#include <vector>
#include <iostream>

struct A {
    int n;
    A(int n_) : n(n_) {}
};

int main() {
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    std::vector<int> int_vec(arr, arr+10);
    std::vector<A> A_vec(int_vec.begin(), int_vec.end());

    for( std::vector<A>::iterator it=A_vec.begin(); it!=A_vec.end(); ++it )
        std::cout<< it->n <<" ";
    std::cout<<std::endl;
}

Ответы [ 3 ]

11 голосов
/ 09 февраля 2012

C ++ январь 2012 г. черновик:

§ 23.2.3 / 3 [sequence.reqmts] .... i и j обозначают итераторы, удовлетворяющие входному итератору требования и относятся к элементам, неявно преобразуемым в value_type , [i, j) обозначает допустимый диапазон ....

X (i, j)
X a (i, j)
Требуется : T должен быть EmplaceConstructible в X от * я. Для вектора, если итератор не соответствует форварду Требования итератора (24.2.5), T также должен быть MoveInsertable в X. Каждый итератор в диапазоне [i, j) должен быть разыменован ровно один раз.
post : расстояние (начало (), конец ()) == расстояние (i, j) Создает последовательность контейнер, равный диапазону [i, j)

Корен обратил мое внимание на цитируемый вами раздел:

§ 23.3.6.2/8 [vector.cons] template <class InputIterator> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
Эффекты : Создает вектор, равный диапазону [первый, последний), используя указанный распределитель.
Сложность : делает только N обращений к конструктору копирования T (где N - расстояние между первым и последним) и не перераспределяет, если итераторы первый и последний имеют прямой, двунаправленный или категории произвольного доступа. Это делает заказ N обращений к конструктору копирования T и перераспределению журналов заказов (N), если они являются просто входными итераторами.

находится в специфичной для вектора области, а технически должно переопределить первый раздел. Тем не менее, я считаю, что эта ссылка на конструктор копирования ошибочна, и, чтобы быть педантичным, упоминание конструкторов копирования максимально усложняется, и поэтому 0 обращений к конструктору копирования (только с использованием конструктора преобразования) мне кажется действительным. Это менее ясно, чем я хотел бы.

Xeo обратил мое внимание на тот факт, что Активные проблемы стандартного базового языка C ++, редакция 78 имеет проблему ( 535 ) о том, как в стандарте «Многие из положений о построении копирования сформулированы так, чтобы ссылаться только на« конструкторы копирования ».« Это явно плохая формулировка ». Каждое использование термина« конструктор копирования »в Стандарте должно быть изучено, чтобы определить, относится ли оно строго к Копировать конструкторы или любой конструктор, используемый для копирования. (Аналогичная проблема относится к «операторам копирования», которые имеют такое же отношение к шаблонам функций оператора присваивания.) «Таким образом, исправление этой неверной формулировки находится в их списке дел.

1 голос
/ 09 февраля 2012

Вы можете даже пойти дальше. Этот код тоже отлично работает:

#include <vector>
#include <iostream>

struct A {
    int n;
    int v;
    A(int n_, int v_ = 0) : n(n_), v(v_) {}
};

int main() {
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    std::vector<A> A_vec(arr, arr+10);

    for( std::vector<A>::iterator it=A_vec.begin(); it!=A_vec.end(); ++it )
        std::cout<< it->n <<" ";
    std::cout<<std::endl;
}

Как вы отметили в стандарте, в §23.3.6.2:

Делает только N вызовов конструктору копирования из T

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

0 голосов
/ 09 февраля 2012

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

template <class InputIterator> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());

должно работать во всех компиляторах.

[Редактировать] Похоже, это требует большего объяснения. Как реализовать описанный выше конструктор Vector?

template <class InputIterator>
vector(InputIterator first, InputIterator last,
            const Allocator& = Allocator())
{
 for(InputIterator i = first; i != last; i++)
 {
   push_back(*i);  // Or whatever way to add to vector.
 }
} 

Теперь любая де-ссылка и попытка добавить ее в локальное контейнерное хранилище * i приведут к созданию конструктора копирования типа * i (скажем, типа T (т. Е. Вектора). Другими словами, реализация должна сделать копия объекта * i и добавление его во внутреннюю коллекцию объектов (что бы это ни было). Таким образом, определение / реализация шаблона будет окончательно расширено до чего-то вроде «T x (* i)». Здесь на подопечных это просто языковой аспект. C ++ не различает, действительно ли * i имеет тип T или * i является типом, который можно неявно преобразовать в T.

Это не должно быть явно указано в стандарте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...