Подтвердить что ты не робот

Разрешено ли преобразование с конструктором шаблонов std::vector с итераторами?

В стандарте С++ 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;
}
4b9b3361

Ответ 1

Из проекта С++ Jan 2012:

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

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

Coren привлекло мое внимание, что раздел, который вы цитировали:

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

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

Xeo привлек мое внимание к тому, что С++ Standard Core Language Active Issues, версия 78 имеет проблему (535) о том, как в стандарте "многие из положений о конструкции копирования сформулированы для ссылки только для" копирования конструкторов "." И это, очевидно, плохая формулировка ". Каждое использование термина" конструктор копирования "в стандарте должно быть проверено, чтобы определить, строго ли оно применяется для копирования конструкторов или любого конструктора, используемого для копирования. (Аналогичная проблема относится к" операторам присваивания копий ", которые имеют одинаковую связь с шаблонами функций оператора присваивания.)" Итак, исправление этой плохой формулировки относится к их списку дел.

Ответ 2

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

#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

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

Ответ 3

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

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

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

[Edit] Похоже, это требует большего объяснения. Как реализовать описанный выше конструктор 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.
 }
} 

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

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