代码之家  ›  专栏  ›  技术社区  ›  Ashwin Nanjappa

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

  •  22
  • Ashwin Nanjappa  · 技术社区  · 16 年前

    我假设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。

    数组的这种用法是否包含STL函数 通过C++/STL ? 如果是,像数组这样的古老结构如何适应模板迭代器、容器和函数的大STL计划?此外,在这种用法中是否有任何警告或细节需要程序员注意 关于

    11 回复  |  直到 16 年前
        1
  •  42
  •   Johannes Schaub - litb    16 年前

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

    • 输入迭代器 :仅向前和一次通过,仅读取
    • 输出迭代器 :仅向前和一次通过,并且仅写入

    • :仅向前和读/写
    • 双向迭代器 :向前和向后,以及读/写
    • :一口气向前和向后任意步数,读/写

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

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

    • 引用-对值类型的引用
    • 指针-指向值\u类型的指针
    • difference_type-两个迭代器之间的距离是什么类型(由返回) std::distance
    • iterator_category-这是一个标记类型:它被定义为表示迭代器类型的类型。任何一个 std::input_iterator_tag std::random_access_iterator_tag . 算法可以使用它来重载不同类型的迭代器(如 对于随机访问迭代器来说速度更快,因为它可以直接返回 a - b )

    iterator_traits 模板,并专门用于指针。因此,如果您想获得任何迭代器的值类型,您可以

    iterator_traits<T>::value_type
    

    无论它是指针还是其他迭代器,它都将为您提供该迭代器的值\类型。

    std::vector<T>::iterator 可以是 T*

        2
  •  18
  •   CAdaker    16 年前

    事实上,一个可能的实现 std::vector<T>::iterator 就是让它成为一个 T* .

    当然,对于一个数组,您将没有有用的 begin() end()

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

        3
  •  7
  •   David Thornley    16 年前

    指针完成了成为随机访问迭代器所需的一切工作。因此,它是一个随机访问迭代器,可以在STL算法中使用。你可以看看向量实现;你很可能会发现 vector<whatever>::iterator 是一个 whatever * .

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

        4
  •  4
  •   bayda    16 年前

    标准是否允许使用带有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< int, 3 > a = { 9, 8, 7 };
    cerr << "Sum: " << accumulate( a.begin(), a.end(), 0, plus<int>()) << endl;
    
        5
  •  2
  •   pauljwilliams    16 年前

    与其使用数组,然后担心将它们传递给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
  •   timday    16 年前

    这个 introduction to boost::array (一个用于传统阵列的简单模板包装器 defines begin() / end()

        7
  •  2
  •   Community CDub    8 年前

    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] 然后信息就丢失了

    @利特:你说得对。我做过这个测试,但它是对数组的引用,而不是数组。谢谢你指出这一点。

    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...
    }
    
        8
  •  1
  •   Edouard A.    16 年前

        9
  •  1
  •   Luc Hermitte    16 年前

    指针模型 Trivial Iterator ,以及来自阵列模型的指针 Random Access Iterator

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

        10
  •  0
  •   Mykola Golubyev    16 年前

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

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

    就像指向数组的常规指针一样 指向最后一个元素的值 超过一个元素的最后一个元素

        11
  •  -1
  •   graphitemaster    14 年前

    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;