Bir bildirim, std ad alanını etkileyebilir mi?


96
#include <iostream>
#include <cmath>

/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
    return a > 0? -a : a;
}

int main() {
    int a = abs(-5);
    int b = std::abs(-5);
    std::cout<< a << std::endl << b << std::endl;
    return 0;
}

Çıktının -5ve olmasını bekliyordum 5, ancak çıktı -5ve -5.

Acaba bu dava neden olacak?

Bunun kullanımıyla bir ilgisi var mı stdveya ne?


1
Uygulamanız absyanlış.
Richard Critten

31
@RichardCritten Konu bu. OP neden bu bozuk absetkileri eklemeyi soruyor std::abs().
HolyBlackCat

11
İlginç, olsun 5ve 5clang, -5ve -5gcc ile.
Rakete1111

10
Cmake bir derleyici değil, bir derleme sistemidir. Cmake'yi çeşitli derleyicilerle derlemek için kullanabilirsiniz.
HolyBlackCat

5
Muhtemelen basitçe işlevinize sahip olmanızı tavsiye ederdim return 0- bu, insanların işlevi istemeden yanlış uyguladığınızı düşünmelerini önler ve istenen ve gerçek davranışı daha net hale getirirdi.
Bernhard Barker

Yanıtlar:


92

Dil belirtimi , uygulamaların <cmath>standart işlevleri küresel ad alanında bildirerek (ve tanımlayarak) gerçekleştirmesine ve daha sonra bunları kullanım std-bildirimleri aracılığıyla ad alanına getirmesine izin verir . Bu yaklaşımın kullanılıp kullanılmadığı belirtilmemiştir.

20.5.1.2 Başlıklar
4 [...] C ++ standart kitaplığında, bildirimler (C'de makro olarak tanımlanan adlar dışında) ad alanının ad alanı kapsamı (6.3.6) içindedir std. Bu adların (Madde 21'den 33'e ve Ek D'ye eklenen herhangi bir aşırı yük dahil) ilk olarak genel ad alanı kapsamında beyan edilip edilmediği ve daha sonra stdaçık kullanım beyanları (10.3.3) ile ad alanına enjekte edilip edilmediği belirtilmemiştir .

Görünüşe göre, bu yaklaşımı izlemeye karar veren uygulamalardan biriyle uğraşıyorsunuz (örn. GCC). Yani sizin uygulama sağlar ::absiken, std::absbasitçe için "anlamına gelir" ::abs.

Bu durumda geriye kalan bir soru, standarda ek olarak neden ::abskendinizinkini de beyan edebildiğiniz ::abs, yani neden çoklu tanımlama hatası olmadığıdır. Bu, bazı uygulamalar tarafından sağlanan başka bir özellikten kaynaklanıyor olabilir (örneğin, GCC): standart işlevleri zayıf semboller olarak ilan ederler , böylece onları kendi tanımlarınızla "değiştirmenize" izin verirler.

Bu iki faktör birlikte gözlemlediğiniz etkiyi yaratır: Zayıf sembolün yer değiştirmesi ::absde yerine geçmesine neden olur std::abs. Bunun dil standardı ile ne kadar uyumlu olduğu farklı bir hikaye ... Her halükarda, bu davranışa güvenmeyin - dil tarafından garanti edilmez.

GCC'de bu davranış aşağıdaki minimalist örnekle yeniden üretilebilir. Bir kaynak dosya

#include <iostream>

void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }

Başka bir kaynak dosya

#include <iostream>

void foo();
namespace N { using ::foo; }

void foo() { std::cout << "Goodbye!" << std::endl; }

int main()
{
  foo();
  N::foo();
}

Bu durumda , ikinci kaynak dosyadaki yeni ::foo( "Goodbye!") tanımının da davranışını etkilediğini gözlemleyeceksiniz N::foo. Her iki çağrı da çıkacaktır "Goodbye!". Ve tanımını ::fooikinci kaynak dosyadan kaldırırsanız , her iki çağrı da "orijinal" tanımına ::foove çıktıya gönderilir "Hello!".


Yukarıdaki 20.5.1.2/4 tarafından verilen izin, uygulamasını basitleştirmek içindir <cmath>. Uygulamaların basitçe C stilini içermesine <math.h>, ardından işlevleri yeniden bildirmesine stdve C ++ 'ya özgü bazı eklemeler ve ince ayarlar eklemesine izin verilir . Yukarıdaki açıklama, sorunun iç mekaniğini doğru bir şekilde tanımlıyorsa, bunun büyük bir kısmı , işlevlerin C tarzı sürümleri için zayıf sembollerin değiştirilebilirliğine bağlıdır .

Biz sadece global yerine eğer Not intile doublebu çıkışı olacak - Yukarıdaki programda, (GCC altında) kodu "beklendiği gibi" davranacaktır -5 5. Bu, C standart kitaplığının işlevi olmadığı için abs(double)olur. Kendimizinkini ilan ederek abs(double)hiçbir şeyin yerini almayız.

Ama geçtikten sonra eğer intbirlikte doublebiz de geçiş absiçin fabs, orijinal garip davranış tüm ihtişamıyla (çıkış yeniden görüntülenecektir -5 -5).

Bu, yukarıdaki açıklama ile tutarlıdır.


cmath'ın kaynağında görebildiğim using ::abs;gibi using ::asin;, bildirimi geçersiz kılabilirsiniz, belirtilmesi gereken bir başka nokta da, std ad alanı işlevlerinde tanımlananların int için değil, double , float
Take_Care_

2
Standart bakış açısından, davranış [extern.names] / 4'e göre tanımsızdır .
xskxzr

Ama #include<cmath>kodumdaki kodu sildiğimde aynı cevabı aldım. '
Peter

@Peter Peki o zaman std :: abs'i nereden alıyorsun? - Başka bir ekleme yoluyla eklenmiş olabilir, bu noktada bu açıklamaya geri dönersiniz. (Bir başlığın doğrudan veya dolaylı olarak dahil edilmiş olması derleyici için önemli değildir.)
RM

@Peter: absiçinde de beyan <cstdlib>edilebilir, bu da aracılığıyla örtük olarak dahil edilebilir <iostream>. Kendinizinkini kaldırmayı absve hala derlenip derlenmediğini görmeyi deneyin .
AnT

13

Kodunuz tanımsız davranışa neden oluyor.

C ++ 17 [harici.adlar] / 4:

Dış bağlantı ile bildirilen C standart kitaplığındaki her işlev imzası, hem harici "C" hem de harici "C ++" bağlantısıyla bir işlev imzası olarak veya genel ad alanında ad alanı kapsamının bir adı olarak kullanılmak üzere uygulamaya ayrılmıştır.

Bu nedenle, Standard C kütüphanesi işleviyle aynı prototipe sahip bir işlev yapamazsınız int abs(int);. Gerçekte hangi başlıkları eklediğinizden veya bu başlıkların C kütüphane adlarını global ad alanına da koyup koymadığına bakılmaksızın.

Ancak, absfarklı parametre türleri sağlarsanız aşırı yüklenmesine izin verilir .


1
"veya genel ad alanındaki ad alanı kapsamının adı olarak", bu nedenle genel ad alanında aşırı yüklenemez.
xskxzr

@xskxzr Alıntı yaptığınız metnin yorumundan emin değilim; kullanıcının genel ad alanında bu addan herhangi bir şey beyan edemeyeceği anlamına gelirse, o zaman alıntı yaptığım metnin önceki bölümü, [extern.names] / 3'ün çoğunda olduğu gibi gereksiz olacaktır. Bu da beni burada başka bir şeyin amaçlandığını düşünmeye sevk ediyor.
MM
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.