Czy to jest poprawny C ++?
int main() {
constexpr auto sz = __func__ - __func__;
return sz;
}
GCC i MSVC uważają, że jest w porządku, Clang uważa, że tak nie jest: Compiler Explorer .
Wszystkie kompilatory zgadzają się, że ten jest w porządku: Eksplorator kompilatorów .
int main() {
constexpr auto p = __func__;
constexpr auto p2 = p;
constexpr auto sz = p2 - p;
return sz;
}
Clangowi znów się to nie podoba, ale inni się z tym zgadzają : Eksplorator kompilatorów
int main() {
constexpr auto p = __func__;
constexpr auto p2 = __func__;
constexpr auto sz = p2 - p;
return sz;
}
Co tu jest Myślę, że arytmetyka niepowiązanych wskaźników jest nieokreślonym zachowaniem, ale __func__
zwraca ten sam wskaźnik, prawda? Nie jestem pewien, więc pomyślałem, że mogę to przetestować. Jeśli dobrze pamiętam, std::equal_to
mogę porównać niepowiązane wskaźniki bez niezdefiniowanego zachowania:
#include <functional>
int main() {
constexpr std::equal_to<const char*> eq{};
static_assert(eq(__func__, __func__));
}
Clang uważa, że eq(__func__, __func__)
nie jest stałym wyrażeniem, chociaż std::equal_to::operator()
jest constexpr . Inne kompilatory nie narzekają: Eksplorator kompilatorów
Clang też tego nie skompiluje. Skarży się, że __func__ == __func__
nie jest stałym wyrażeniem: Eksplorator kompilatora
int main() {
static_assert(__func__ == __func__);
}
__func__
jest tak-jeślistatic const char __func__[] = "function-name";
i to jest akceptowane odpowiednik Demo ...__func__
i użyjesz jej w static_assert ...__func__
całkowicie usunąć z oceny constexpr.Odpowiedzi:
__func__
w C ++ jest identyfikatorem. W szczególności odnosi się do określonego obiektu. Od [dcl.fct.def.general] / 8 :Jako zmienna predefiniowana lokalnie dla funkcji , ta definicja (jak gdyby) pojawia się na początku bloku funkcyjnego. Jako takie, wszelkie zastosowania
__func__
w tym bloku będą odnosić się do tej zmiennej.Jeśli chodzi o część „dowolny inny obiekt”, zmienna definiuje obiekt.
__func__
nazywa obiekt zdefiniowany przez tę zmienną. Dlatego w ramach funkcji wszystkie zastosowania__func__
nazwy nazywają tę samą zmienną. Nieokreślone jest to, czy zmienna ta jest obiektem odrębnym od innych obiektów.Oznacza to, że jeśli korzystasz z funkcji o nazwie
foo
i użyłeś literału"foo"
w innym miejscu problemu, nie jest zabronione, aby implementacja miała zmienną będącą__func__
tym samym obiektem, który"foo"
zwraca literał . Oznacza to, że standard nie wymaga, aby każda funkcja, w której się__func__
pojawia, musi przechowywać dane oddzielnie od samego literału łańcucha.Teraz zasada „jak gdyby” w C ++ pozwala implementacjom na odchylenie od tego, ale nie mogą tego zrobić w sposób, który byłby wykrywalny. Tak więc, chociaż sama zmienna może mieć, ale nie musi, odrębny adres od innych obiektów, zastosowania
__func__
tej samej funkcji muszą zachowywać się tak, jakby odnosiły się do tego samego obiektu.Clang wydaje się nie wdrażać w
__func__
ten sposób. Wygląda na to, że implementuje to tak, jakby zwróciło literał ciąg znaków wartości nazwy funkcji. Dwa odrębne literały łańcuchowe nie muszą odnosić się do tego samego obiektu, więc odejmowanie do nich wskaźników to UB. Nieokreślone zachowanie w stałym kontekście ekspresji jest źle sformułowane.Jedyne, co sprawia, że waham się powiedzieć, że Clang jest w 100% błędny, to [temp.arg.nontype] / 2 :
Widzisz, wydaje się, że pozwala to na pewne fałszowanie przy implementacji. Oznacza to, że chociaż
__func__
technicznie może być stałym wyrażeniem, nie można go użyć w parametrze szablonu. Jest traktowany jak dosłowny ciąg znaków, mimo że technicznie jest zmienną.Więc na pewnym poziomie powiedziałbym, że standard mówi z obu stron jego ust.
źródło
__func__
może być stałym wyrazem we wszystkich przypadkach mojego pytania, prawda? Więc kod powinien się skompilować.__func__
adres będzie taki sam jak adres innego obiektu, a za drugim razem__func__
nie będzie? To prawda, że nie oznacza to, że adres różni się między tymi dwoma instancjami, ale nadal jestem zdezorientowany!__func__
nie jest makrem; jest to identyfikator, który nazywa określoną zmienną, a zatem konkretny obiekt. Dlatego każde użycie__func__
tej samej funkcji powinno skutkować uzyskaniem wartości glvalue odnoszącej się do tego samego obiektu. Lub bardziej konkretnie, nie można go wdrożyć w taki sposób, aby tak nie było.new
, nigdzie nie dojdzie, dopóki program się nie zakończy.