“Açıklık” nedir ve ne zaman kullanmalıyım?


237

Son zamanlarda span<T>benim kodları 's kullanmak için öneriler aldım, ya da burada span' s sözde bir tür konteyner kullanan sitede bazı cevaplar gördük . Ama - C ++ 17 standart kütüphanesinde böyle bir şey bulamıyorum.

Peki bu gizemli nedir span<T>ve standart değilse neden (veya ne zaman) kullanmak iyi bir fikirdir?


std::spanC ++ 17 veya C ++ 20 için geçerlidir. Ayrıca bkz. P0122R5, span: nesnelerin dizileri için güvenli sınırlar . Bu dili gerçekten hedeflemek istiyor musunuz? Derleyicilerin yetişmesi yıllar alacaktır.
jww

6
@jww: span'lar C ++ 11 ile oldukça kullanılabilir ... gsl::spanaksine std::span. Ayrıca aşağıdaki yanıtıma bakın.
einpoklum

Ayrıca cppreference.com adresinde de belgelenmiştir: en.cppreference.com/w/cpp/container/span
Keith Thompson

1
@KeithThompson: 2017 yılında değil ...
einpoklum

@jww Tüm derleyiciler şimdi C ++ 20 modunda std :: span <> özelliğini desteklemektedir. Ve birçok üçüncü parti kütüphanesinde açıklık mevcuttur. Haklıydın - yıllardı: kesin olmak için 2 yıl.
Contango

Yanıtlar:


272

Bu ne?

A span<T>:

  • TBellekte bir yerde bitişik değerler dizisinin çok hafif bir soyutlaması .
  • Temel struct { T * ptr; std::size_t length; }olarak bir grup kolaylık yöntemi ile.
  • Sahip olmayan bir tür (yani, " değer türü" yerine "başvuru türü"): Hiçbir zaman hiçbir şey ayırmaz veya dağıtmaz ve akıllı işaretçileri canlı tutmaz.

Eskiden array_viewve daha önce de biliniyordu array_ref.

Ne zaman kullanmalıyım?

İlk olarak, ne zaman değil zaman kullanılır?

  • Kodunda kullanmayın sadece başlangıç ve bitiş Yineleyicilerin herhangi çiftini sürebilir böyle std::sort, std::find_if, std::copyve bu süper jenerik şablonlu fonksiyonların hepsi.
  • Kodunuz için uygun olduğunu bildiğiniz standart bir kütüphane kabınız (veya bir Boost kabınız vb.) Varsa kullanmayın. Hiçbirini takviye etmek amaçlanmamıştır.

Şimdi ne zaman kullanılacağı için:

Kullanım span<T>(sırasıyla span<const T>) yerine bir serbest duran T*(sırasıyla const T*kendisi için uzunluk değerine sahip). Yani, aşağıdaki gibi işlevleri değiştirin:

  void read_into(int* buffer, size_t buffer_size);

ile:

  void read_into(span<int> buffer);

Neden kullanmalıyım? Neden iyi bir şey?

Oh, açıklıklar harika! Kullanarak span...

  • bu işaretçi + uzunluk / başlangıç ​​+ bitiş işaretçisi kombinasyonuyla çalışabileceğiniz gibi, süslü, pezevenk bir standart kütüphane kabıyla çalışabileceğiniz anlamına gelir, örneğin:

    • for (auto& x : my_span) { /* do stuff */ }
    • std::find_if(my_span.begin(), my_span.end(), some_predicate);

    ... ama çoğu konteyner sınıfının kesinlikle üstesinden gelmez.

  • derleyici bazen sizin için daha fazla iş yapalım. Örneğin, bu:

    int buffer[BUFFER_SIZE];
    read_into(buffer, BUFFER_SIZE);

    bu olur:

    int buffer[BUFFER_SIZE];
    read_into(buffer);

    ... yapmasını istediğiniz şeyi yapacak. Ayrıca bkz . Kılavuz P.5 .

  • const vector<T>&verilerinizin bellekte bitişik olmasını beklediğinizde işlevlere geçmenin makul bir alternatifidir . Artık yüksek ve güçlü C ++ guruları tarafından azarlanmak yok!

  • statik analizi kolaylaştırır, böylece derleyici aptalca hataları yakalamanıza yardımcı olabilir.
  • çalışma zamanı sınır kontrolü için hata ayıklama-derleme enstrümantasyonuna izin verir (yani, spanyöntemleri içinde bazı sınır kontrol kodu olacaktır #ifndef NDEBUG... #endif)
  • kodunuzun (yayılma alanını kullanan) sivri belleğe sahip olmadığını belirtir.

C ++ temel yönergelerindespan bulabileceğiniz s kullanmak için daha fazla motivasyon var - ancak sürüklemeyi yakalayın.

Neden standart kütüphanede değil (C ++ 17'den itibaren)?

Standart kütüphanede - ancak sadece C ++ 20'den itibaren. Bunun nedeni, sadece 2015'ten beri şekillenmekte olan C ++ temel yönergeler projesi ile birlikte tasarlanan mevcut haliyle hala oldukça yeni olmasıdır . (Yorum yapanların belirttiği gibi, daha erken bir geçmişi vardır.)

Peki, henüz standart kütüphanede değilse nasıl kullanırım?

Temel Yönergeler Destek Kütüphanesi'nin (GSL) bir parçasıdır . Uygulamalar:

  • Microsoft / Neil Macintosh'un GSL'si bağımsız bir uygulama içerir:gsl/span
  • GSL-Lite , tüm GSL'nin (o kadar büyük değil, endişelenmeyin) tek başlıklı bir uygulamasıdır span<T>.

GSL uygulaması genellikle C ++ 14 desteği uygulayan bir platform varsaymaktadır [ 14 ]. Bu alternatif tek başlıklı uygulamalar GSL olanaklarına bağlı değildir:

Bu farklı açıklık uygulamalarının, birlikte geldikleri yöntem / destek işlevlerinde bazı farklılıklar olduğunu unutmayın; ve ayrıca standart C ++ 20 kütüphanesine giren versiyondan biraz farklı olabilirler.


Daha fazla okuma: C ++ 17, P0122R7: span: span: Nesnelerin dizileri için sınır güvenli görünümlerini Neal Macintosh ve Stephan J. Lavavej'den önce son resmi teklifte tüm detayları ve tasarım hususlarını bulabilirsiniz . Yine de biraz uzun. Ayrıca, C ++ 20'de, yayılma karşılaştırma semantiği değişti ( bu kısa makalenin ardından Tony van Eerd).


2
Genel bir aralığı standartlaştırmak (yineleyici + sentinel ve yineleyici + uzunluk, hatta yineleyici + sentinel + uzunluk) standartlaştırmak ve yayılmayı basit bir typedef yapmak daha mantıklı olacaktır. Çünkü, bilirsiniz, bu daha geneldir.
Deduplicator

3
@Deduplicator: Aralıklar C ++ 'a geliyor, ancak mevcut teklif (Eric Niebler tarafından) Kavramlar için destek gerektiriyor. Yani C ++ 20'den önce değil.
einpoklum

8
@ HảiPhạmLê: Diziler hemen işaretçi olarak bozulmaz. yapmayı deneyin std::cout << sizeof(buffer) << '\n've 100 sizeeof (int) elde edersiniz.
einpoklum

4
@Jim std::arraybir kaptır, değerlere sahiptir. spansahibi değil
Caleth

3
@Jim: std::arraytamamen farklı bir canavar. Uzunluğu derleme zamanında sabittir ve Caleth'in açıkladığı gibi bir referans türü yerine bir değer türüdür.
einpoklum

1

@einpoklum bir şey tanıtılması oldukça iyi bir iş yapar spanolduğunu burada onun cevabını . Bununla birlikte, cevabını okuduktan sonra bile, yeni birisinin hala tam olarak cevaplanmayan bir dizi düşünce akışı sorusuna sahip olması kolaydır, örneğin:

  1. Bir Nasıl spanbir C diziden farklı? Neden sadece bunlardan birini kullanmıyorsunuz? Görünüşe göre boyutu da bilinenlerden biri ...
  2. Bekle, bu kulağa benziyor std::array, bundan nasıl spanfarklı?
  3. Ah, beni hatırlatan bir değil std::vectorbir benzeri std::arrayde?
  4. Kafam çok karışık. :( Nedir span?

İşte, bununla ilgili bazı ek netlik:

HIS CEVAP DOĞRUDAN QUOTE - İLE MY EKLENEN İÇİNDE KALIN :

Bu ne?

A span<T>:

  • TBellekte bir yerde bitişik değerler dizisinin çok hafif bir soyutlaması .
  • Temel olarak bir grup kolaylık yöntemi ile tek bir yapı { T * ptr; std::size_t length; }. (Not Bu belirgin şekilde farklı olan std::array<>bir çünkü spanyöntemlerin erişimci kolaylık, karşılaştırılabilir sağlayan std::arraybir cihaz aracılığıyla, yazmak için işaretçiT ve tip uzunluğu (elementlerin sayısı) Tise, std::arraybir ya da daha fazlasına sahip bir gerçek bir kaptır değerleri Çeşidi T.)
  • Sahip olmayan bir tür (yani, " değer türü" yerine "başvuru türü"): Hiçbir zaman hiçbir şey ayırmaz veya dağıtmaz ve akıllı işaretçileri canlı tutmaz.

Eskiden array_viewve daha önce de biliniyordu array_ref.

Bu cesur parçalar kişinin anlayışı için kritik öneme sahiptir, bu yüzden onları kaçırmayın veya yanlış okumayın! A spanbir C-dizisi dizisi DEĞİLDİR, ne de bir C-dizisi tipi Tartı dizinin uzunluğu (bu esasen std::array kabın ne olacağıdır), NOR işaretçilerden oluşan C-dizisidir türüne Tartı uzunluğu, bunun yerine a, tek tek içeren yapı tipine işaretçiT ve uzunluğu olan, (tip elemanların sayısı Tişaretçi yazmak için bu bitişik bellek bloğunda) Tiçin puan! Bu şekilde, bir eklentispanişaretçiyi ve uzunluğu saklamak için değişkenler ve kullandığınız kolaylık erişimci işlevleri spansağlar.

Bu AKSİNE bir olduğunu std::array<>, çünkü std::array<>tüm bitişik blok için aslında ayırır bellek ve AKSİNE olan std::vector<>bir nedeni std::vectortemelde sadece std::arrayayrıca yaptığı dinamik büyüyen her seferinde doldurur (genelde boyut olarak iki katına) ve ona başka bir şey eklemeye çalıştığınızda . A std::arrayboyutu sabittir ve a spanişaret ettiği bloğun belleğini bile yönetmez, sadece bellek bloğunu gösterir, bellek bloğunun ne kadar uzun olduğunu bilir, bir C-dizisinde hangi veri türünün olduğunu bilir ve bitişik bellekteki öğelerle çalışmak için kolaylık erişimci işlevleri sağlar .

Bu ise C parçası ++ standardı:

std::spanC ++ 20'den itibaren C ++ standardının bir parçasıdır. Belgelerini buradan okuyabilirsiniz: https://en.cppreference.com/w/cpp/container/span . Google'ın bugünabsl::Span<T>(array, length) C ++ 11 veya sonraki sürümlerinde nasıl kullanılacağını görmek için aşağıya bakın.

Özet Açıklamalar ve Temel Referanslar:

  1. std::span<T, Extent>( Extent= "sekanstaki öğe sayısı veya std::dynamic_extentdinamikse". Bir yayılma alanı yalnızca belleğe işaret eder ve erişimi kolaylaştırır, ancak yönetmez!):
    1. https://en.cppreference.com/w/cpp/container/span
  2. std::array<T, N>( sabit bir boyuta sahip olduğuna dikkat edin N!):
    1. https://en.cppreference.com/w/cpp/container/array
    2. http://www.cplusplus.com/reference/array/array/
  3. std::vector<T> (otomatik olarak gerektiğinde otomatik olarak boyut olarak büyür):
    1. https://en.cppreference.com/w/cpp/container/vector
    2. http://www.cplusplus.com/reference/vector/vector/

Nasıl Can I Kullanım spanC ++ 11 veya daha sonra bugün ?

Google, dahili C ++ 11 kitaplıklarını "Abseil" kitaplıkları biçiminde açık kaynaklı hale getirmiştir. Bu kütüphane, yarının özelliklerini bugün kullanabilmeniz için, C ++ 11 ve sonraki sürümlerinde çalışan C ++ 14'ten C ++ 20'ye ve ötesine özellikler sağlamayı amaçlamaktadır. Onlar söylüyor:

C ++ Standardı ile uyumluluk

Google, C ++ 14, C ++ 17 ve ötesinde bulunan özelliklerle eşleşen veya bunlarla yakından eşleşen birçok soyutlama geliştirmiştir. Bu soyutlamaların Abseil sürümlerini kullanmak, kodunuz henüz bir C ++ 11 dünyasında yaşam için hazır olmasa bile, şimdi bu özelliklere erişmenizi sağlar.

İşte bazı önemli kaynaklar ve bağlantılar:

  1. Ana site: https://abseil.io/
  2. https://abseil.io/docs/cpp/
  3. GitHub deposu: https://github.com/abseil/abseil-cpp
  4. span.hüstbilgi ve absl::Span<T>(array, length)şablon sınıfı: https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L189

1
Bence önemli ve faydalı bilgiler getirdiniz, teşekkürler!
Gui Lima
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.