Özel kaplar fonksiyonları bitirmek/begin özgür olmalıdır?
Her zamanki kurallara göre oynayan, özel bir konteyner sınıfı (yani algoritmalar çalışır uslu ile genel kod vs. STL ile çalışır.), oluştururken C 03 yineleyici destek ve üye fonksiyonları bitirmek/begin uygulamak için yeterli oldu.
C 11 döngü ve std için iki yeni kavram menzilli tabanlı tanıtır::başlangıç-bitiş. Aralığı tabanlı C 03 kaplar destek aralığı tabanlı yani döngü için üye işlevleri sona/başlamak anlar, kutudan çıktığı için. Algoritmalar için önerilen yöntem (göre 'modern C kodu' Herb Sutter tarafından) Yazılı std kullanmaktır::üye işlevi yerine başlar.
Ancak, bu noktada sormam gerekiyor - önerilen yol arayıp bir tam begin() fonksiyonu (yani std::begin(c)) veya güvenmek ADL ve aramayı başlatın(c)?
ADL bu durumda işe yaramaz - std beri görünüyor:: (c) c delegeler başlar.(mümkünse) başlamak, her zamanki ADL yararları uygulamak için görünmüyor. Ve eğer herkes ADL güvenmeye başlarsa, tüm özel kaplar ekstra başlamak uygulamak zorunda()/son() gerekli kendi ad ücretsiz işlevleri. Ancak, çeşitli kaynaklardan/son başlamak için niteliksiz çağrılar önerilen yöntem (60 *yani*) olduğunu ima görünüyor.
C 11 yolu nedir? Konteyner kütüphane yazarlar ekstra dersler koşulsuz destek için fonksiyonları bitirmek/begin write ad std; std yokluğunda aramaları bitir/başlamalıdır::başlamak;?
CEVAP
Çeşitli yaklaşımlar, kendi artıları ve eksileri ile her vardır. Maliyet-fayda analizi ile aşağıda üç yaklaşım.
Özel ADL ile üye olmayan begin()
/ end()
İlk alternatif sağlar üye olmayan begin()
end()
fonksiyon şablonları içinde legacy
ad güçlendirme gerekli işlevselliği üzerine herhangi bir sınıf veya sınıf şablonu bunu sağlayabilir, ama örneğin yanlış adlandırma kuralları. Kodu çağıran o ADL bu yeni işlevler bulmak için güvenebilirsiniz. Kod (@Xeo tarafından yoruma dayalı) örnek:
// LegacyContainerBeginEnd.h
namespace legacy {
// retro-fitting begin() / end() interface on legacy
// Container class template with incompatible names
template<class C>
auto begin(Container& c) -> decltype(c.legacy_begin())
{
return c.legacy_begin();
}
// similarly for begin() taking const&, cbegin(), end(), cend(), etc.
} // namespace legacy
// print.h
template<class C>
void print(C const& c)
{
// bring into scope to fall back on for types without their own namespace non-member begin()/end()
using std::begin;
using std::end;
// works for Standard Containers, C-style arrays and legacy Containers
std::copy(begin(c), end(c), std::ostream_iterator<decltype(*begin(c))>(std::cout, " ")); std::cout << "\n";
// alternative: also works for Standard Containers, C-style arrays and legacy Containers
for (auto elem: c) std::cout << elem << " "; std::cout << "\n";
}
Artıları:tamamen çalışan tutarlı ve veciz çağırma jenerik
- herhangi bir Standart Konteyner ve üyesi
.begin()
.end()
tanımlayan kullanıcı tipleri için çalışır - C stili diziler için çalışıyor
- (ayrıca için . için entegre edilebilir ^strong>dizi için döngüler!) herhangi biri içinsınıf şablonuKaynak kodunda değişiklik yapılmasına gerek olmadan
.begin()
end()
üye olmayanlegacy::Container<T>
İnş: birçok yerde gerektirir bildirimleri kullanarak
std::begin
std::end
Her kesin arama kapsamı içine taşınmış C stili diziler için seçenekler (şablon başlıkları ve genel rahatsızlık için potansiyel hatadır) geri düşmek gerekli
Özel ile ADL-üye olmayan adl_begin()
adl_end()
İkinci bir alternatif olduğu için saklanması kullanarak bildirimlerinin önceki çözüm ayrı bir adl
Ad tarafından sağlanması olmayan üye işlev şablonları adl_begin()
adl_end()
, sonra da bulundu aracılığıyla ADL. Kod (@Yakk tarafından yoruma dayalı) örnek:
// LegacyContainerBeginEnd.h
// as before...
// ADLBeginEnd.h
namespace adl {
using std::begin; // <-- here, because otherwise decltype() will not find it
template<class C>
auto adl_begin(C && c) -> decltype(begin(std::forward<C>(c)))
{
// using std::begin; // in C 14 this might work because decltype() is no longer needed
return begin(std::forward<C>(c)); // try to find non-member, fall back on std::
}
// similary for cbegin(), end(), cend(), etc.
} // namespace adl
using adl::adl_begin; // will be visible in any compilation unit that includes this header
// print.h
# include "ADLBeginEnd.h" // brings adl_begin() and adl_end() into scope
template<class C>
void print(C const& c)
{
// works for Standard Containers, C-style arrays and legacy Containers
std::copy(adl_begin(c), adl_end(c), std::ostream_iterator<decltype(*adl_begin(c))>(std::cout, " ")); std::cout << "\n";
// alternative: also works for Standard Containers, C-style arrays and legacy Containers
// does not need adl_begin() / adl_end(), but continues to work
for (auto elem: c) std::cout << elem << " "; std::cout << "\n";
}
Artıları: tutarlı çağırma tamamen jenerik olarak çalışır
- @Xeo önerisi için aynı profesyoneller gibi
- tekrarlanan açıklamalar kullanarak (KURU) kapsüllü olmuştur
İnşküçük bir ayrıntı
adl_begin()
/adl_end()
24* /end()
*kadar kısa ve öz.- belki de deyimsel açık olmasına rağmen)
- C 14 dönüş türü indirimi bekleyen de 26* /
std::end
*ile isim kirletir
NOT: Eğer bu gerçekten iyileşirse emin değilim önceki yaklaşımı üzerine.
Açıkça std::begin()
std::end()
Her yerde özellikli
begin()
/ end()
ayrıntı neyse vaz edildikten sonra, neden geristd::begin()
/ std::end()
nitelikli çağrıları gitmiyorsun? Örnek kod:
// LegacyIntContainerBeginEnd.h
namespace std {
// retro-fitting begin() / end() interface on legacy IntContainer class
// with incompatible names
template<>
auto begin(legacy::IntContainer& c) -> decltype(c.legacy_begin())
{
return c.legacy_begin();
}
// similary for begin() taking const&, cbegin(), end(), cend(), etc.
} // namespace std
// LegacyContainer.h
namespace legacy {
template<class T>
class Container
{
public:
// YES, DOCUMENT REALLY WELL THAT THE EXISTING CODE IS BEING MODIFIED
auto begin() -> decltype(legacy_begin()) { return legacy_begin(); }
auto end() -> decltype(legacy_end()) { return legacy_end(); }
// rest of existing interface
};
} // namespace legacy
// print.h
template<class C>
void print(C const& c)
{
// works for Standard Containers, C-style arrays as well as
// legacy::IntContainer and legacy::Container<T>
std::copy(std::begin(c), std::end(c), std::ostream_iterator<decltype(*std::begin(c))>(std::cout, " ")); std::cout << "\n";
// alternative: also works for Standard Containers, C-style arrays and
// legacy::IntContainer and legacy::Container<T>
for (auto elem: c) std::cout << elem << " "; std::cout << "\n";
}
Artılarıneredeyse jenerik olarak çalışan tutarlı çağırma
- herhangi bir Standart Konteyner ve üye
.begin()
.end()
tanımlayan kullanıcı tipleri için çalışır - C stili diziler için çalışıyor
İnşbiraz ayrıntılı ve güçlendirme ve bakım değil, genel bir sorundur
std::begin()
/std::end()
begin()
/end()
biraz daha ayrıntılı- sadece iş (aynı zamanda için . için entegre edilebilir ^strong>dizi için döngüler!) herhangi biri içinsınıf
LegacyContainer
üye olmayan.begin()
end()
(ve kaynak kodu! olduğu için) üye olmayan fonksiyon açık uzmanlık sağlayarak şablonlarıbegin()
namespace std
45* - sadece üzerine entegre edilebilirsınıf şablonları47* *doğrudan
begin()
/end()
LegacyContainer<T>
şablonları için kullanılabilir) kaynak kod içinde üye işlevler ekleyerek.namespace std
hile işlev şablonları kısmen özel olamaz çünkü burada çalışmıyor.
Kullanmak ne?
ADL yaklaşımı ile üye olmayan begin()
/ end()
bir kapsayıcı kendi ad olduğu deyimsel C 11 yaklaşım, özellikle genel işlevleri gerektirir güçlendirme üzerinde eski sınıflar ve sınıf şablonları. Kullanıcı sağlayan üye olmayan swap()
fonksiyonlar için aynı deyim.
Kod için tek Standart Konteyner kullanır veya C stili diziler std::begin()
std::end()
her yerde daha ayrıntılı aramalar pahasına kullanarak bildirimleri, tanıtmadan denilebilir. Bu yaklaşım bile entegre edilebilir ama namespace std
(sınıf türleri için) veya-kaynak modifcations (sınıf şablonları) ile işe yaramaz gerektirir. Bu yapılabilir, ama bu bakım sıkıntıya değmez.
Olmayan genel kod, nerede konteyner soru bilinen kodlama zamanı, bir tane bile itimat ADL için Standart Kaplar sadece ve açıkça hak std::begin
/ std::end
C-tarzı diziler. Bazı arama tutarlılığı kaybeder ama kullanarak bildirimleri kaydeder.
Özel karakterler, normal ifadelerde ka...
Özel yardımcı yöntemler eğer statik ol...
Python tanımlama özel modül fonksiyonl...
Şifre 1 büyük harf, 1 Özel Karakter, a...
Logger özel statik veya değil olmalıdı...