转换是否允许使用std :: vector的模板构造函数获取迭代器?

时间:2012-02-09 17:07:46

标签: c++ stl vector

在C ++ 11标准的第23.3.6.2节[vector.cons]中,如下所述:

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

9 效果:使用指定的分配器构造一个等于范围[first,last)的向量。
  10 复杂性:仅对N的复制构造函数进行N次调用(其中N是firstlast之间的距离)如果迭代器的第一个和最后一个是向前的,则不进行重新分配,双向或随机访问类别。如果它们只是输入迭代器,它会使命令N调用T的复制构造函数和命令log(N)重新分配。

(此文本也存在于较旧的标准中)。一方面,它不要求解除引用InputIterator应该导致存储在向量中的相同类型的值。另一方面,它讲述了使用复制构造函数,这种类型暗示了相同的类型。

我的问题是:如果可以在类型之间进行转换,那么使用此构造函数的不同类型的元素序列是否有效?需要参考该标准。

例如,以下代码正常at 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 个答案:

答案 0 :(得分:11)

来自C ++ Jan 2012草案:

  

§23.2.3/ 3 [sequence.reqmts] .... i和j表示满足输入迭代器的迭代器   要求和指可隐式转换为的元素   value_type ,[i,j)表示有效范围....

     

X(i,j)
  X a(i,j)
  需要:T应该是EmplaceConstructible到X.   来自* i。对于vector,如果迭代器不符合前向   迭代器要求(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());
  效果:使用指定的分配器构造一个等于[first,last]范围的向量。
  复杂性:仅对T 的复制构造函数进行N 调用(其中N是第一个和最后一个之间的距离),如果迭代器的第一个和最后一个是正向的,则不进行重新分配或随机访问类别。如果它们只是输入迭代器,它会使命令N调用T的复制构造函数和命令log(N)重新分配。

位于矢量特定区域,技术上应覆盖第一部分。但是,我相信这个对复制构造函数的引用是错误的,并且是迂腐的,提到复制构造函数的复杂性是最大的,因此0调用复制构造函数(仅使用转换构造函数)在我看来是有效的。这不像我希望的那么清晰。

Xeo让我注意到这样一个事实:C++ Standard Core Language Active Issues, Revision 78有一个问题(535)是关于如何在标准中“关于复制结构的许多规定仅用于指代“复制构造函数。”'这显然是不好的措辞。“应该检查标准中”复制构造函数“一词的每次使用,以确定它是否严格适用于复制构造函数或任何用于复制的构造函数。 (类似的问题适用于“复制赋值运算符”,它与赋值运算符函数模板具有相同的关系。)“因此,纠正这种不良措辞是在他们的待办事项清单上。

答案 1 :(得分:1)

你甚至可以走得更远。这段代码也可以正常工作:

#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

复制构造函数进行N次调用

=&GT;这个构造函数迭代每个元素并使用复制构造函数,因此,只要你有一个工作的复制构造函数,它就可以在每个编译器上正常工作。

答案 2 :(得分:0)

由于编译器无法区分尝试使用类型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的复制构造函数(让我们说类型T(即向量)。换句话说,实现必须使对象* i的副本并将其添加到内部对象集合(无论它是什么)。 因此,模板定义/实现最终将扩展为“T x(* i)”。这里的病房只是一个语言方面。 C ++不区分* i是否实际上是类型T或* i是可以隐式转换为T的类型。

这不需要在标准中明确说明。