Argüman bağımlı aramanın ne olduğuna dair iyi açıklamalar nelerdir? Birçok insan buna Koenig Lookup da diyor.
Tercihen bilmek istiyorum:
- Neden iyi bir şey?
- Neden kötü bir şey?
- O nasıl çalışır?
std::cout << "Hello world";
derlemek olmaz
Argüman bağımlı aramanın ne olduğuna dair iyi açıklamalar nelerdir? Birçok insan buna Koenig Lookup da diyor.
Tercihen bilmek istiyorum:
std::cout << "Hello world";
derlemek olmaz
Yanıtlar:
Koenig Arama veya Bağımsız Değişken Bağımlı Arama , nitelenmemiş adların C ++ 'da derleyici tarafından nasıl arandığını açıklar.
C ++ 11 standardı § 3.4.2 / 1 şunları belirtir:
Bir işlev çağrısındaki (5.2.2) postfix-ifadesi niteliksiz bir id olduğunda, olağan niteliksiz arama (3.4.1) sırasında dikkate alınmayan diğer ad alanları aranabilir ve bu ad alanlarında ad alanı kapsamı arkadaş işlev bildirimleri ( 11.3) aksi görülemez. Aramadaki bu değişiklikler, bağımsız değişken türlerine (ve şablon şablonu bağımsız değişkenleri için şablon bağımsız değişkeninin ad alanı) bağlıdır.
Daha basit terimlerle Nicolai Josuttis 1 :
Bir veya daha fazla bağımsız değişken türü işlevin ad alanında tanımlanmışsa işlevler için ad alanını nitelemeniz gerekmez.
Basit bir kod örneği:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass);
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
Yukarıdaki örnekte ne bir using
-declare ya da using
-dektif yoktur, ancak yine de derleyici, Koenig araması uygulayarak nitelenmemiş adı doSomething()
ad alanında bildirilen işlev olarak doğru olarak tanımlar .MyNamespace
Algoritma derleyiciye yalnızca yerel kapsama değil, aynı zamanda argümanın türünü içeren ad alanlarına da bakmasını söyler. Böylece, yukarıdaki kodda, derleyici obj
işlevin argümanı olan nesnenin doSomething()
ad alanına ait olduğunu bulur MyNamespace
. Bu nedenle, bildirimini bulmak için bu ad alanına bakar doSomething()
.
Yukarıdaki basit kod örneğinin gösterdiği gibi, Koenig araması programcıya kolaylık ve kullanım kolaylığı sağlar. Koenig araması olmadan, programcı üzerinde tam olarak nitelenmiş adları tekrar tekrar belirtmek veya bunun yerine çok sayıda- using
beyanı kullanmak için bir ek yük olurdu .
Koenig aramasına aşırı güvenmek semantik sorunlara yol açabilir ve programcıyı bazen tetikte tutabilir.
std::swap
İki değeri değiştirmek için standart bir kütüphane algoritması olan örneği ele alalım . Koenig aramasıyla, bu algoritmayı kullanırken dikkatli olunmalıdır çünkü:
std::swap(obj1,obj2);
aşağıdakilerle aynı davranışı göstermeyebilir:
using std::swap;
swap(obj1, obj2);
ADL ile, swap
işlevin hangi sürümü çağrılırsa kendisine iletilen bağımsız değişkenlerin ad alanına bağlı olur.
Orada bir ad varsa A
ve eğer A::obj1
, A::obj2
& A::swap()
mevcut sonra ikinci örnek bir çağrı ile sonuçlanacaktır A::swap()
kullanıcı istediğini olmayabilir.
Ayrıca, eğer bir nedenden ötürü tanımlanmışsa A::swap(A::MyClass&, A::MyClass&)
ve std::swap(A::MyClass&, A::MyClass&)
tanımlanmışsa, ilk örnek çağıracaktır, std::swap(A::MyClass&, A::MyClass&)
ancak ikincisi derlenmeyecektir çünkü swap(obj1, obj2)
belirsiz olacaktır.
Çünkü eski AT&T ve Bell Labs araştırmacısı ve programcısı Andrew Koenig tarafından tasarlandı .
Standart C ++ 03/11 [basic.lookup.argdep]: 3.4.2 Bağımsız Değişkene bağlı ad arama.
1 Koenig aramasının tanımı Josuttis'in The C ++ Standard Library: A Tutorial ve Reference kitabında tanımlandığı gibidir.
std::swap
tek alternatif eklemek olacaktır çünkü aslında bunu yapmak zorunda std::swap
sizin için şablon işlevi açık ihtisas A
sınıfından. Ancak A
sınıfınız bir şablonsa, açık uzmanlaşma yerine kısmi uzmanlaşma olacaktır. Ve şablon fonksiyonunun kısmi uzmanlaşmasına izin verilmez. Aşırı yüklemesi eklemek std::swap
alternatif olabilir, ancak açıkça yasaktır ( std
ad alanına bir şeyler ekleyemezsiniz ). Yani ADL bunun için tek yoldur std::swap
.
std::swap()
biraz geri görünüyor. std::swap()
Tipe özgü aşırı yük yerine, seçildiğinde sorunun olmasını bekliyorum A::swap()
. İle örnek std::swap(A::MyClass&, A::MyClass&)
yanıltıcı görünüyor. çünkü std
bir kullanıcı türü için belirli bir aşırı yüklenme asla Bence harika bir örnek olduğunu sanmıyorum.
MyNamespace::doSomething
sadece arıyor değil ::doSomething
.
Koenig Arama'da, bir işlev kendi ad alanını belirtmeden çağrılırsa, bir işlevin adı , bağımsız değişken türlerinin tanımlandığı ad alanlarında da aranır. Bu nedenle, Argüman Bağımlı adı Arama olarak da bilinir , kısaca basitçe ADL .
Koenig Lookup yüzünden bunu yazabiliriz:
std::cout << "Hello World!" << "\n";
Aksi takdirde şunu yazmamız gerekir:
std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");
bu gerçekten çok fazla yazıyor ve kod gerçekten çirkin görünüyor!
Başka bir deyişle, Koenig Lookup'un bulunmadığı durumlarda, bir Hello World programı bile karmaşık görünüyor.
std::cout
etkinleştirmek için yeterli olan işleve ilişkin bir argüman olduğunu unutmayın . Bunu fark ettin mi?
ostream<<
(argüman olarak neye ihtiyaç duyduğu ve neyin geri döndüğü gibi). 2) Tam nitelikli isimler ( std::vector
veya gibi std::operator<<
). 3) Argümana Bağımlı Arama'nın daha ayrıntılı bir çalışması.
std::endl
değişken olarak alabilen işlev aslında bir üye işlevidir. Her neyse, "\n"
bunun yerine kullanırsam std::endl
, cevabım doğru. Yorum için teşekkürler.
f(a,b)
çağırır . Dolayısıyla, ikinci argüman olarak kabul edilen böyle bir serbest işlev yoktur . Bağımsız değişken olarak alan ve yazmak zorunda olduğunuz üye işlevidir . ve ikinci argüman olarak özgür bir işlev olduğu için çalışır; de işe yarar. std::operator<<(std::cout, std::endl);
std::endl
std::endl
std::cout.operator<<(std::endl);
char const*
"\n"
'\n'
Belki de nedeniyle başlamak ve ancak o zaman nasıl yapılacağına bakmak en iyisidir.
İsim alanları tanıtıldığında, fikir isim alanlarında tanımlanmış olan her şeye sahip olmaktı, böylece ayrı kütüphaneler birbirlerini etkilemiyordu. Ancak bu operatörlerle bir sorun yarattı. Örneğin, aşağıdaki koda bakın:
namespace N
{
class X {};
void f(X);
X& operator++(X&);
}
int main()
{
// define an object of type X
N::X x;
// apply f to it
N::f(x);
// apply operator++ to it
???
}
Tabii ki yazmış olabilirsiniz N::operator++(x)
, ancak bu operatörün aşırı yüklenmesinin tüm noktasını yenerdi. Bu nedenle, derleyicinin operator++(X&)
kapsamda olmamasına rağmen bulmasına izin veren bir çözüm bulunmalıydı . Öte yandan, operator++
çağrıyı belirsiz hale getirebilecek başka, ilgisiz bir ad alanında tanımlanan başka bir tane bulamamalıdır (bu basit örnekte belirsizlik elde edemezsiniz, ancak daha karmaşık örneklerde de yapabilirsiniz). Çözüm, bağımsız değişkene bağlı olduğu için (daha doğrusu, bağımsız değişkenin türüne göre) bu şekilde adlandırılan Bağımsız Değişken Bağımlı Arama (ADL) idi. Şema Andrew R. Koenig tarafından icat edildiğinden, buna genellikle Koenig araması da denir.
İşin püf noktası, işlev çağrıları için, normal ad aramaya ek olarak (kullanım noktasında kapsamdaki adları bulur), işleve verilen herhangi bir bağımsız değişken türünün kapsamlarında ikinci bir arama yapılır. Eğer yazarsanız Yani yukarıdaki örnekte, x++
ana yılında, o arar operator++
küresel kapsamda, fakat buna ek türü kapsamında sadece x
, N::X
, tanımlandı, yani içinde namespace N
. Ve orada bir eşleşme bulur operator++
ve bu nedenle x++
çalışır. Başka operator++
başka ad alanında tanımlanan, diyelim N2
ki, bulunmayacak. ADL, ad alanlarıyla sınırlı olmadığından f(x)
, N::f(x)
in yerine de kullanabilirsiniz main()
.
Bence bu konuda her şey iyi değil. Derleyici satıcıları da dahil olmak üzere insanlar, bazen talihsiz davranışlarından dolayı hakaret ediyorlar.
ADL, C ++ 11'deki aralık aralığı döngüsünün büyük bir revizyonundan sorumludur. ADL'nin neden bazen istenmeyen etkilere sahip olabileceğini anlamak için, yalnızca argümanların tanımlandığı ad alanlarının değil, aynı zamanda argümanların şablon argümanlarının, fonksiyon türlerinin parametre tiplerinin / sivri uç türlerinin bu argümanların işaretçi türlerinin de dikkate alındığını düşünün ve benzerleri.
Boost kullanan bir örnek
std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);
Her ikisi de std::begin
(ADL kullanarak std::vector
) ve boost::begin
bulunduğunda (ADL kullanarak boost::shared_ptr
) kullanıcı boost.range kitaplığını kullanırsa bu bir belirsizliğe neden oldu .
std::begin
ad alanı belirsizliğini temizler.