“Bağımsız Değişken Bağımlı Arama” nedir (diğer adıyla ADL veya “Koenig Araması”)?


177

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?




Yanıtlar:


225

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

O nasıl çalışır?

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 objiş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().

Koenig aramasının avantajı nedir?

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- usingbeyanı kullanmak için bir ek yük olurdu .

Neden Koenig aramasının eleştirisi?

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, swapiş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 Ave 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.

Diğer bilgiler:

Neden “Koenig araması” deniyor?

Çünkü eski AT&T ve Bell Labs araştırmacısı ve programcısı Andrew Koenig tarafından tasarlandı .

Daha fazla okuma:


1 Koenig aramasının tanımı Josuttis'in The C ++ Standard Library: A Tutorial ve Reference kitabında tanımlandığı gibidir.


11
@AlokSave: Cevap için +1, ancak trivia doğru değil. Koenig, burada itiraf ettiği gibi
ADL'yi

20
Koenig Algoritması eleştirisindeki örnek, Koenig aramasının bir "özelliği" kadar "özelliği" olarak düşünülebilir. Std :: swap () yöntemini bu şekilde kullanmak yaygın bir deyimdir: Daha özel bir A :: swap () sürümü sağlanmamışsa bir 'std :: swap () kullanarak' sağlayın. A :: takası () bir uzman sürümü mevcutsa, biz normall olur istiyoruz biri çağrılacak söyledi. Bu, swap () çağrısı için daha fazla jeneriklik sağlar, çünkü derleme ve çalışma çağrısına güvenebiliriz, ancak varsa daha özelleştirilmiş sürüme de güvenebiliriz.
Anthony Hall

6
@anthrond Daha fazlası var. İle std::swaptek alternatif eklemek olacaktır çünkü aslında bunu yapmak zorunda std::swapsizin için şablon işlevi açık ihtisas Asınıfından. Ancak Ası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::swapalternatif olabilir, ancak açıkça yasaktır ( stdad alanına bir şeyler ekleyemezsiniz ). Yani ADL bunun için tek yoldur std::swap.
Adam Badura

1
"Koenig aramasının avantajı" altında aşırı yüklü operatörlerden bahsetmeyi beklerdim. ile örnek 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ü stdbir kullanıcı türü için belirli bir aşırı yüklenme asla Bence harika bir örnek olduğunu sanmıyorum.
Arvid

1
@gsamaras ... Ya? Hepimiz fonksiyonun asla tanımlanmadığını görebiliriz. Hata mesajınız bunun işe yaradığını kanıtlıyor, çünkü MyNamespace::doSomethingsadece arıyor değil ::doSomething.
Monica'nın Davası

70

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.


12
İkna edici örnek.
Anthony Hall

10
@AdamBadura: Lütfen ADL'yi std::coutetkinleştirmek için yeterli olan işleve ilişkin bir argüman olduğunu unutmayın . Bunu fark ettin mi?
Nawaz

1
@meet: Sorunuzun bu alanda sağlanamayan uzun bir cevaba ihtiyacı var. Bu yüzden sadece aşağıdaki gibi konuları okumanızı tavsiye edebilirim: 1) imzası ostream<<(argüman olarak neye ihtiyaç duyduğu ve neyin geri döndüğü gibi). 2) Tam nitelikli isimler ( std::vectorveya gibi std::operator<<). 3) Argümana Bağımlı Arama'nın daha ayrıntılı bir çalışması.
Nawaz

2
@WorldSEnder: Evet, haklısın. Bağımsız std::endldeğ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.
Nawaz

2
@ Yıkıcı: Çünkü biçim çağrısının işlev çağrısı serbest bir işlevi 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::endlstd::endlstd::cout.operator<<(std::endl);char const*"\n"'\n'
Nawaz

30

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 N2ki, bulunmayacak. ADL, ad alanlarıyla sınırlı olmadığından f(x), N::f(x)in yerine de kullanabilirsiniz main().


Teşekkürler! Asla gerçekten neden orada olduğunu anlamadım!
user965369

20

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::beginbulunduğunda (ADL kullanarak boost::shared_ptr) kullanıcı boost.range kitaplığını kullanırsa bu bir belirsizliğe neden oldu .


Her zaman ilk olarak şablon argümanlarını düşünmenin ne faydası olduğunu merak ettim.
Dennis Zickefoose

ADL'nin yalnızca operatörler için önerildiğini söylemek doğru mudur ve diğer işlevler için ad alanlarını açıkça yazmak daha iyidir?
balki

Ayrıca, argümanların temel sınıflarının ad alanlarını da dikkate alıyor mu? (eğer tabii ki delirirse).
Alex B

3
nasıl düzeltilir? std :: kullanın?
paulm

2
@paulm Evet, std::beginad alanı belirsizliğini temizler.
Nikos
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.