Salah satu manfaat std::begin
dan std::end
adalah mereka berfungsi sebagai titik ekstensi untuk mengimplementasikan antarmuka standar untuk kelas eksternal.
Jika Anda ingin menggunakan CustomContainer
kelas dengan rentang berbasis untuk fungsi loop atau templat yang diharapkan .begin()
dan .end()
metode, Anda jelas harus menerapkan metode tersebut.
Jika kelas memang menyediakan metode tersebut, itu tidak masalah. Jika tidak, Anda harus memodifikasinya *.
Ini tidak selalu layak, misalnya ketika menggunakan perpustakaan eksternal, terutama yang komersial dan sumber tertutup.
Dalam situasi seperti itu, std::begin
dan std::end
sangat berguna, karena seseorang dapat menyediakan API iterator tanpa memodifikasi kelas itu sendiri, tetapi kelebihan fungsi bebas.
Contoh: misalkan Anda ingin menerapkan count_if
fungsi yang mengambil wadah alih-alih sepasang iterator. Kode tersebut mungkin terlihat seperti ini:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
Sekarang, untuk setiap kelas yang ingin Anda gunakan dengan kebiasaan ini count_if
, Anda hanya perlu menambahkan dua fungsi gratis, daripada memodifikasi kelas-kelas itu.
Sekarang, C ++ memiliki mechanisim yang disebut Argument Dependent Lookup
(ADL), yang membuat pendekatan seperti itu bahkan lebih fleksibel.
Singkatnya, ADL berarti, bahwa ketika kompiler menyelesaikan fungsi yang tidak memenuhi syarat (yaitu fungsi tanpa namespace, seperti begin
bukan std::begin
), ia juga akan mempertimbangkan fungsi yang dideklarasikan dalam ruang nama argumen. Sebagai contoh:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
Dalam hal ini, tidak masalah bahwa nama yang memenuhi syarat adalah some_lib::begin
dan some_lib::end
- karena CustomContainer
ada some_lib::
juga, kompiler akan menggunakan kelebihan itu di count_if
.
Itu juga alasan untuk memiliki using std::begin;
dan using std::end;
masuk count_if
. Ini memungkinkan kita untuk menggunakan tanpa pengecualian begin
dan end
, karenanya memungkinkan ADL dan
memungkinkan kompiler untuk memilih std::begin
dan std::end
ketika tidak ada alternatif lain ditemukan.
Kita bisa makan cookie dan memiliki cookie - yaitu memiliki cara untuk menyediakan implementasi kustom begin
/ end
sementara kompiler dapat kembali ke yang standar.
Beberapa catatan:
Untuk alasan yang sama, ada fungsi serupa lainnya: std::rbegin
/ rend
,
std::size
dan std::data
.
Seperti jawaban lain menyebutkan, std::
versi memiliki kelebihan untuk array kosong. Itu berguna, tetapi hanya kasus khusus dari apa yang saya jelaskan di atas.
Menggunakan std::begin
dan berteman adalah ide yang bagus ketika menulis kode templat, karena ini membuat templat-templat tersebut lebih umum. Untuk non-templat Anda mungkin juga menggunakan metode, jika berlaku.
PS Saya tahu bahwa postingan ini sudah hampir 7 tahun. Saya menemukan itu karena saya ingin menjawab pertanyaan yang ditandai sebagai duplikat dan menemukan bahwa tidak ada jawaban di sini yang menyebutkan ADL.