簡體   English   中英

C ++ STL:可以使用STL函數透明地使用數組嗎?

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

我假設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計划? 此外,程序員應該注意這些用法中是否有任何警告或細節?

好吧,你問一個陣列。 你可以很容易地得到一個指向它的元素的指針,所以它基本上歸結為指針是否可以透明地與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* 指針甚至是迭代器的一個很好的例子。 因為它非常簡單但同時又如此強大以至於它可以在一個范圍內迭代。

該標准設計了迭代器,使其感覺和行為盡可能像指針一樣。 此外,由於迭代器基於模板,唯一相關的是迭代器類型具有定義的適當運算符。 結果是指針開箱即用的行為就像隨機訪問迭代器一樣。

實際上, std::vector<T>::iterator一個可能實現就是使它成為T*

當然,對於數組,您將沒有有用的begin()end()方法來查找有效的迭代器范圍,但這是C樣式數組始終存在的問題。

編輯:實際上,正如評論和其他答案中所提到的,如果數組不是動態的並且沒有衰減成指針,則可以為數組實現這些函數。 但我的基本觀點是,你必須比使用標准容器時更加小心。

簡短回答:STL算法通常被定義為與各種迭代器一起使用。 迭代器由它的行為定義:它必須可以用*解除引用,它必須可以用++遞增,以及各種其他的東西也定義它是什么類型的迭代器(最常見的是隨機訪問)。 請記住,STL算法是模板,因此問題是語法之一。 類似地,使用operator()定義的類實例在語法上與函數一樣,因此它們可以互換使用。

指針執行隨機訪問迭代器所需的一切。 因此,它是一個隨機訪問迭代器,可以在STL算法中使用。 你可以看看矢量實現; 你很可能會發現vector<whatever>::iterator是一個whatever *

這不會使數組成為有效的STL容器,但它確實使指針成為有效的STL迭代器。

是否允許標准允許使用具有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;

只是評論Mykola的答案

數組不是指針,即使它們傾向於很容易衰變成指針。 編譯器在數組上的信息多於容器上的信息:

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...
}

而不是使用數組然后擔心將它們傳遞給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,您可以這樣做,這要歸功於向量與數組的二進制兼容性。

boost::array (傳統數組的簡單模板包裝,也定義了 STL兼容的迭代器類型和begin() / end()等)的介紹包含了一些關於它們與STL的兼容程度的有趣討論。

是的,這是故意的。 迭代器可以實現為指針,因此您可以使用指針作為迭代器。

指針模型為Trivial Iterator ,指針來自數組模型Random Access Iterator 所以是的,這是完全合法的。

如果您對每個S(T)L算法的使用限制感興趣,請熟悉迭代器模型

作為int,[]可以被視為指針。 在C ++中,指針可以遞增,然后指向下一個元素。 並且可以比較指針,然后指針可以用作迭代器。

標准24.1部分中指出了迭代器的要求。 指針符合他們。 這是其中一些

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

正如指向數組的常規指針一樣,它保證指針值指向數組的最后一個元素,因此對於任何迭代器類型,都有一個迭代器值指向相應容器的最后一個元素。

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM