__Func__ işaretçilerin iki sınır örneğinin farkı hala sınır mı?


14

Bu geçerli C ++ mı?

int main() {
    constexpr auto sz = __func__ - __func__;
    return sz;
}

GCC ve MSVC iyi olduğunu düşünüyor, Clang bunun olmadığını düşünüyor: Derleyici Gezgini .


Tüm derleyiciler bunun uygun olduğunu kabul eder: Derleyici Gezgini .

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = p;
    constexpr auto sz = p2 - p;
    return sz;
}

Clang yine bunu sevmiyor, ancak diğerleri bununla iyi durumda: Compiler Explorer

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = __func__;
    constexpr auto sz = p2 - p;
    return sz;
}

Burada ne var? Ben ilişkisiz işaretçiler aritmetik tanımsız davranış olduğunu, ancak __func__aynı işaretçiyi döndürür, hayır? Emin değilim, bu yüzden test edebileceğimi düşündüm. Doğru hatırlıyorsam, std::equal_totanımlanmamış davranış olmadan ilgisiz işaretçileri karşılaştırabilirsiniz:

#include <functional>

int main() {
    constexpr std::equal_to<const char*> eq{};
    static_assert(eq(__func__, __func__));
}

Clang , constexpreq(__func__, __func__) olmasına rağmen sabit bir ifade olmadığını düşünüyor . Diğer derleyiciler şikayet etmiyor: Derleyici Gezginistd::equal_to::operator()


Clang bunu da derlemeyecek. __func__ == __func__Sabit bir ifade olmayan şikayetler : Derleyici Gezgini

int main() {
    static_assert(__func__ == __func__);
}

Gönderen Function_definition , __func__olarak -eğer olduğunu static const char __func__[] = "function-name";eşdeğer kabul edilir ve bu Demo ...
Jarod42

İlginçtir, bir constexpr değişkeni ile başlatırsanız ve static_assert'te __func__kullanırsanız çalışır ...
florestan

@ Jarod42 Peki bu Clang'da bir hata mı?
Ayxan

gibi @florestan bu ? Clang ile de derlenmeyecek. Sorudaki 2. ve 3. örneklerim, bahsettiğiniz yöntem. Biri derler, diğeri derlemez.
Ayxan

1
Ayrıca bkz CWG1962 kaldırmak olabilir __func__constexpr değerlendirme tamamen.
Davis Herring

Yanıtlar:


13

__func__C ++ 'da bir tanımlayıcıdır. Özellikle, belirli bir nesneye gönderme yapar. Kaynaktan [dcl.fct.def.general] / 8 :

İşlev-yerel önceden tanımlanmış değişken _­_­func_­_­, formun bir tanımı gibi tanımlanır

static const char __func__[] = "function-name";

burada işlev-adı, uygulama tanımlı bir dizedir. Böyle bir değişkenin, programdaki diğer herhangi bir nesnenin adresinden farklı bir adresinin olup olmadığı belirtilmez.

Bir As fonksiyon-yerel önceden tanımlanmış bir değişken , bu tanım (sanki) işlevi bloğun başında görünür. Bu nedenle, __func__bu blok içindeki herhangi bir kullanım bu değişkene atıfta bulunacaktır.

"Başka herhangi bir nesne" bölümüne gelince, bir değişken bir nesneyi tanımlar. __func__bu değişken tarafından tanımlanan nesneyi adlandırır. Bu nedenle, bir işlev içinde, tüm __func__değişkenler aynı değişkeni adlandırır. Tanımlanmamış olan, bu değişkenin diğer nesnelerden farklı bir nesne olup olmadığıdır .

Diğer bir deyişle, adlı bir fooişlevdeyseniz ve sorundaki "foo"başka bir yerde değişmezi kullandıysanız, bir uygulamanın değişkeni __func__değişmezin "foo"döndürdüğü nesne ile aynı olması yasaktır . Yani, standart, __func__görünen her fonksiyonun veri dizgesinin kendisinden ayrı saklanmasını gerektirmez .

Şimdi, C ++ 'ın 'sanki' kuralı uygulamaları bu sapmaya izin verir, fakat bunlar olamaz tespit olacak bir şekilde yapıyoruz. Bu nedenle, değişkenin kendisi diğer nesnelerden farklı bir adrese sahip olsa da olmasa da __func__, aynı işlevde kullanımları aynı nesneye atıfta bulunmuş gibi davranmalıdır.

Clang __func__bu şekilde uygulanmış görünmüyor . Sanki, işlevin adının bir değer dizesi değişmezini döndürüyormuş gibi görünüyor. İki farklı dize değişmezinin aynı nesneye başvurması gerekmez, bu nedenle onlara işaretçiler çıkarmak UB'dir. Ve sabit bir ifade bağlamında tanımlanmamış davranışlar kötü biçimlendirilmiştir.

Burada Clang'ın % 100 yanlış olduğunu söylemekten çekinmeme neden olan tek şey [temp.arg.nontype] / 2 :

Referans veya işaretçi türünün tür olmayan bir şablon parametresi için, sabit ifadenin değeri atıfta bulunmamalıdır (veya bir işaretçi türü için adresi:):

...

  • önceden tanımlanmış bir _­_­func_­_değişken.

Bakın, bu uygulama tarafından biraz yumuşatmaya izin veriyor gibi görünüyor. Yani, __func__teknik olarak sabit bir ifade olabilirken, bunu bir template parametresinde kullanamazsınız. Teknik olarak bir değişken olmasına rağmen, bir dizgi değişmezi olarak ele alınır.

Yani bir düzeyde, standardın ağzının her iki tarafından da konuştuğunu söyleyebilirim.


Yani, kesinlikle konuşmak __func__, sorumun tüm durumlarında sabit bir ifade olabilir, değil mi? Yani kod derlenmiş olmalıydı.
Ayxan

"Böyle bir değişkenin, programdaki herhangi bir nesnenin adresinden farklı bir adrese sahip olup olmadığı belirtilmemiş." Bölüm? Belirtilmemiş davranış, soyut makinenin davranışında determinizm anlamına gelir. Bağlamsal değerlendirme için sorunlu olabilir mi? __func__Adresin ilk oluşumu için başka bir nesneninkiyle aynıysa ve ikincisinde __func__görülmezse ne olur? Verilmiş, bu adres iki örnek arasında farklı olduğu anlamına gelmez, ama hala kafam karıştı!
Johannes Schaub - litb

@ JohannesSchaub-litb: " Peki ya" Böyle bir değişkenin programdaki diğer herhangi bir nesnenin adresinden farklı bir adrese sahip olup olmadığı belirtilmemiş. "Bölüm? " __func__bir makro değildir; belirli bir değişkeni ve dolayısıyla belirli bir nesneyi adlandıran bir tanımlayıcıdır. Bu nedenle, __func__aynı işlevde herhangi bir kullanım , aynı nesneye gönderme yapan bir glvalue ile sonuçlanmalıdır. Veya daha da önemlisi, durum böyle olmayacak şekilde uygulanamaz.
Nicol Bolas

@Nicol aynı nesneyi ifade eder. Ancak bu nesne bir anda başka bir nesne ile aynı adrese sahip olabilir. Ve diğer anda değil. Bunun bir sorun olduğunu söylemiyorum, ama herkese bu olasılığı hatırlatıyorum. Ve sonuçta, ben de yanılmış olabilirim, bu yüzden bunu düzeltilme veya onaylanma umuduyla da söylüyorum.
Johannes Schaub - litb

@ JohannesSchaub-litb: " Ama bu nesne bir anda başka bir nesne ile aynı adrese sahip olabilir. " Buna C ++ nesne modeli altında izin verilmiyor. İkisi de diğerinin içine yerleştirilmemiş olan iki nesne, aynı anda aynı depolama alanında yaşamları içinde olamaz. Ve söz konusu nesnenin statik depolama süresi vardır, bu nedenle newüzerinde yerleşim kullanmazsanız , program bitene kadar hiçbir yere gitmez.
Nicol Bolas
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.