C++ STL: Can arrays be used transparently with STL functions?

后端 未结 11 1193
半阙折子戏
半阙折子戏 2020-12-15 17:07

I was under the assumption that STL functions could be used only with STL data containers (like vector) until I saw this piece of code:

#include         


        
相关标签:
11条回答
  • 2020-12-15 17:57

    Just a comment on Mykola's answer:

    Arrays are not pointers, even if they tend to decay into pointers really easily. The compiler has more info on an array than on a container:

    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
    }
    

    While you cannot ask about the size of the array, the compiler will resolve it when calling the given templates.

    Now, if you call a function that takes a int a[] (note that there is no size), that is similar to defining an int* parameter, and the size info is lost in the way. The compiler will not be able to determine the size of the array inside the function: the array has decayed into a pointer.

    If , on the other hand, you define the parameter as int a[10] then the information is lost, but you will not be able to call the function with an array of a different size. This is completely different than the C version, at least prior to C99 have not checked lately[*]. In C the compiler will ignore the number in the argument and the signature will be equivalent to the previous version.

    @litb: You are right. I had this test around, but it is with a reference to an array, not with an array. Thanks for pointing it out.

    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...
    }
    
    0 讨论(0)
  • 2020-12-15 18:00

    Instead of using arrays and then worrying about passing them to STL functions (what one might call 'forwards compatibility', and is therefore fragile), IMO you should use std::vector and use its (stable and dependable) backwards compatibility with functions that take arrays, should you ever need to use them.

    So your code becomes:

    #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;
    }
    

    And if you ever need to pass 'a' to a C API you can do so, thanks to vectors binary compatibility with arrays.

    0 讨论(0)
  • 2020-12-15 18:02

    The standard has designed iterators to feel and behave as much like pointers as possible. Also, since iterators are based on templates, the only relevant thing is that the iterator type has the proper operators defined. The result is that pointers will out-of-the-box behave just like random access iterators.

    In fact, a possible implementation of std::vector<T>::iterator is to just make it a T*.

    Of course, for an array you won't have the useful begin() and end() methods to find the valid iterator range, but that's the problem you always have with C style arrays.

    Edit: Actually, as has been mentioned in the comments and other answers, you can implement those functions for arrays if the array is not dynamic and has not decayed into a pointer. But my basic point was that you have to be more careful than when using the standard containers.

    0 讨论(0)
  • 2020-12-15 18:07

    Well, you ask about an array. You can just easily get a pointer to its elements, so it basically boils down to the question whether pointers can be used transparently with STL functions. A pointer actually is the most powerful kind of an iterator. There are different kinds

    • Input iterator: Only forward and one-pass, and only read
    • Output iterator: Only forward and one-pass, and only write

    • Forward iterator: Only forward, and read/write
    • Bidirectional iterator: Forward and backward, and read/write
    • Random access iterator: Arbitrary steps forward and backward in one breath, and read/write

    Now each iterator in the second group supports all the things of all iterators mentioned before it. A pointer models the last kind of iterators - a random access iterator. You may add/subtract an arbitrary integer and you may read and write. And all except the output iterator has a operator-> that can be used to access a member of the element type we iterate over.

    Normally, iterators have several typedefs as members

    • value_type - what the iterator iterates over (int, bool, string, ...)
    • reference - reference to the value_type
    • pointer - pointer to the value_type
    • difference_type - what type the distance between two iterators has (returned by std::distance).
    • iterator_category - this is a tag-type: it is typedefed to a type that represents the kind of the iterator. either std::input_iterator_tag, ..., std::random_access_iterator_tag. Algorithms can use it to overload on different kinds of iterators (like std::distance is faster for random access iterators, because it can just return a - b)

    Now, a pointer of course does not have those members. C++ has an iterator_traits template and specializes it for pointers. So if you want to get the value type of any iterator, you do

    iterator_traits<T>::value_type
    

    And whether it is a pointer or some other iterator, it will give you the value_type of that iterator.

    So - yes, a pointer can very well be used with STL algorithms. As someone else mentioned, even std::vector<T>::iterator can be a T*. A pointer is a very good example of an iterator even. Because it is so exceedingly simple but at the same time so powerful that it can iterate over a range.

    0 讨论(0)
  • 2020-12-15 18:10

    the STL has it hidden stuff. Most of this works thanks to iterators, consider this code:

    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;
    
    0 讨论(0)
提交回复
热议问题