[英]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作為成員
std::distance
返回)。 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.