C ++ STL:可以使用STL函数透明地使用数组吗?

时间:2009-04-03 10:00:18

标签: c++ arrays stl

我假设STL函数只能用于STL数据容器(如vector),直到我看到这段代码:

#include <functional>
#include <iostream>
#include <numeric>
using namespace std;

int main()
{
    int a[] = {9, 8, 7};
    cerr << "Sum: " << accumulate(&a[0], &a[3], 0, plus<int>()) << endl;
    return 0;
}

它在没有任何警告或g ++错误的情况下编译和运行,给出正确的输出和24。

C ++ / STL 标准是否允许使用STL函数允许的数组?如果是,那么像数组这样的古老结构如何适应模板化迭代器,容器和函数的宏STL计划?此外,在这种用法中是否有任何警告或细节,程序员应该小心

11 个答案:

答案 0 :(得分:40)

好吧,你问一个阵列。你可以很容易地得到一个指向它的元素的指针,所以它基本上归结为指针是否可以透明地与STL函数一起使用的问题。指针实际上是迭代器中最强大的一种。有不同的种类

  • 输入迭代器:仅转发和一次通过,仅读取
  • 输出迭代器:仅转发和一次通过,只写

  • 转发迭代器:仅转发和读/写
  • 双向迭代器:前进和后退,以及读/写
  • 随机访问迭代器:一口气向前和向后任意步骤,读/写

现在,第二组中的每个迭代器都支持前面提到的所有迭代器的所有内容。指针模拟最后一种迭代器 - 随机访问迭代器。您可以添加/减去任意整数,您可以读写。除输出迭代器之外的所有迭代器都有operator->,可用于访问我们迭代的元素类型的成员。

通常,迭代器有几个typedef作为成员

  • value_type - 迭代器迭代的内容(int,bool,string,...)
  • reference - 对value_type的引用
  • 指针 - 指向value_type
  • 的指针
  • difference_type - 两个迭代器之间的距离是什么类型(由std::distance返回)。
  • iterator_category - 这是一个标记类型:它被typedefed为一个表示迭代器类型的类型。 std::input_iterator_tag,...,std::random_access_iterator_tag。算法可以使用它来重载不同类型的迭代器(如std::distance对于随机访问迭代器更快,因为它只能返回a - b

现在,指针当然没有这些成员。 C ++有一个iterator_traits模板,专门用于指针。因此,如果要获取任何迭代器的值类型,请执行

iterator_traits<T>::value_type

无论是指针还是其他迭代器,它都会为你提供迭代器的value_type。

所以 - 是的,指针可以很好地与STL算法一起使用。正如其他人提到的,即使std::vector<T>::iterator也可以是T*。指针甚至是迭代器的一个很好的例子。因为它非常简单但同时又如此强大以至于它可以在一个范围内迭代。

答案 1 :(得分:17)

标准设计的迭代器感觉和行为尽可能像指针一样。此外,由于迭代器基于模板,唯一相关的是迭代器类型具有定义的适当运算符。结果是,开箱即用的指针就像随机访问迭代器一样。

事实上,std::vector<T>::iterator的可能实现方法是将其设为T*

当然,对于数组,您将没有有用的begin()end()方法来查找有效的迭代器范围,但这是C样式数组始终存在的问题。

编辑:实际上,正如评论和其他答案中所提到的,如果数组不是动态的并且没有衰减成指针,则可以为数组实现这些函数。但我的基本观点是,你必须比使用标准容器时更加小心。

答案 2 :(得分:7)

简短回答:STL算法通常被定义为与各种迭代器一起使用。迭代器由它的行为定义:它必须可以用*解除引用,它必须可以用++递增,以及各种其他的东西也定义它是什么类型的迭代器(最常见的是随机访问)。请记住,STL算法是模板,因此问题是语法之一。类似地,使用operator()定义的类实例在语法上与函数一样,因此它们可以互换使用。

指针执行随机访问迭代器所需的一切。因此,它是一个随机访问迭代器,可以在STL算法中使用。你可以看看矢量实现;您很可能会发现vector<whatever>::iteratorwhatever *

这不会使数组成为有效的STL容器,但它确实使指针成为有效的STL迭代器。

答案 3 :(得分:3)

  

标准是否允许使用具有STL功能的数组?

是的

  

如果是,那么像数组这样的古老结构如何适应模板化迭代器,容器和函数的宏STL计划?

迭代器的设计与指针的语义相似。

  

此外,程序员应该注意这些用法中是否有任何警告或细节?

我更喜欢下一次使用:

int a[] = {9, 8, 7};
const size_t a_size = lengthof( a );
cerr << "Sum: " << accumulate( a, a + a_size , 0, plus<int>()) << endl;

或者使用boost :: array更好更安全:

boost::array< int, 3 > a = { 9, 8, 7 };
cerr << "Sum: " << accumulate( a.begin(), a.end(), 0, plus<int>()) << endl;

答案 4 :(得分:2)

只是评论Mykola的answer

数组不是指针,即使它们往往很容易衰减成指针。编译器在数组上的信息多于容器上的信息:

namespace array {
   template <typename T, int N>
   size_t size( T (&a)[N] ) {
      return N;
   }
   template <typename T, int N>
   T* begin( T (&a)[N] ) {
      return &a[0];
   }
   template <typename T, int N>
   T* end( T (&a)[N] ) {
      return &a[N];
   }
}
int main()
{
   int theArray[] = { 1, 2, 3, 4 };
   std::cout << array::size( theArray ) << std::endl; // will print 4
   std::cout 
      << std::accumulate( array::begin( theArray ), array::end( theArray ), 0, std::plus<int>() )
      << std::endl; // will print 10
}

虽然您无法询问数组的大小,但编译器会在调用给定模板时解析它。

现在,如果你调用一个带int a[]的函数(注意没有大小),这类似于定义int*参数,并且大小信息会丢失。编译器将无法确定函数内部数组的大小:数组已经衰减为指针。

如果,另一方面,你将参数定义为int a[10]然后信息丢失,但你将无法用一个数组调用该函数不同的大小。这与C版完全不同,至少在C99之前没有检查过[*]。在C中,编译器将忽略参数中的数字,签名将等同于以前的版本。

@litb:你是对的。我有这个测试,但它是对数组的引用,而不是数组。谢谢你指出来。

dribeas@golden:array_size$ cat test.cpp 
void f( int (&x)[10] ) {}
int main()
{
    int array[20];
    f( array ); // invalid initialization of reference of type 'int (&)[10]' from...
}

答案 5 :(得分:2)

不是使用数组然后担心将它们传递给STL函数(可能称之为'转发兼容性',因此很脆弱),IMO你应该使用std :: vector并使用它(稳定可靠)向后兼容性如果您需要使用它们,那么使用带有数组的函数。

所以你的代码变成了:

#include <functional>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;

int main()
{
    vector<int> a(3);
    a[0] = 9;
    a[1] = 8;
    a[2] = 7;
    cerr << "Sum: " << accumulate(a.begin(), a.end(), 0, plus<int>()) << endl;
    return 0;
}

如果你需要将'a'传递给C API,你可以这样做,这要归功于矢量与数组的二进制兼容性。

答案 6 :(得分:2)

introduction to boost::array(传统数组的简单模板包装,也是defines STL兼容的迭代器类型和begin() / end()等)包含一些有趣的讨论与STL的兼容程度。

答案 7 :(得分:1)

是的,这是故意的。迭代器可以实现为指针,因此您可以使用指针作为迭代器。

答案 8 :(得分:1)

指针模型Trivial Iterator,以及来自数组模型Random Access Iterator的指针。是的,这是完全合法的。

如果您对每个S(T)L算法的使用限制感兴趣,请熟悉iterator models

答案 9 :(得分:0)

作为int,[]可以被视为指针。在C ++中,指针可以递增,然后指向下一个元素。并且可以比较指针,然后指针可以用作迭代器。

标准24.1部分中指出了迭代器的要求。指针符合他们。以下是其中一些

  

所有迭代器我都支持表达式* i

     

就像指向数组的常规指针一样   保证有一个指针   指向最后一个元素的值   对于任何迭代器类型,数组都是如此   有一个指向的迭代器值   过去的最后一个元素   相应的容器。

答案 10 :(得分:-1)

STL有隐藏的东西。大部分工作都归功于迭代器,请考虑以下代码:

std::vector<int> a = {0,1,2,3,4,5,6,7,8,9};
// this will work in C++0x use -std=c++0x with gcc
// otherwise use push_back()

// the STL will let us create an array from this no problem
int * array = new int[a.size()];
// normally you could 'iterate' over the size creating
// an array from the vector, thanks to iterators you
// can perform the assignment in one call
array = &(*a.begin());

// I will note that this may not be considered an array
// to some. because it's only a pointer to the vector.
// However it comes in handy when interfacing with C
// Instead of copying your vector to a native array
// to pass to a C function that accepts an int * or
// anytype for that matter, you can just pass the
// vector's iterators .begin().

// consider this C function
extern "C" passint(int *stuff) { ... }

passint(&(*a.begin())); // this is how you would pass your data.

// lets not forget to delete our allocated data
delete[] a;