Widziałem podobny tupt w bieżącym kodzie i starałem się sam, a wszystko działa dobrze. Poszczególne zainteresowanie tutaj jest to, że spodziewam się (*(T*) nullptr) (), aby zawodzić w czasie wykonywania, ale nie. Czy jest tu niezdefiniowane zachowanie i dlaczego to działa?

template<typename T>
inline static int var = (*(T*) nullptr) ();

template<typename T>
int getvar (const T&)
{
    return var<T>;
}

int main ()
{
    int x = getvar ([]() { return 5; });
    return 0;
}

Dzięki!

c++
0
Rado 14 grudzień 2019, 22:23

1 odpowiedź

Najlepsza odpowiedź

Tak, ten program ma niezdefiniowane zachowanie. Wynikiem wyrażenia lambda jest rwalue znamiennej klasy typu [exprim.Lambda.Closure] / 1 ma przeciążony operator wywołania funkcji [Expr.prim.lubda.Closure] / 3. Tak więc, co zasadniczo robisz w swoim przykładzie tutaj

(*(T*) nullptr) ()

Czy jesteś wywoływany operator () typu lambda (który jest funkcją członka niezmiennego) na obiekcie, który nie istnieje. Możesz znaleźć więcej informacji na temat tego, dlaczego jest tutaj: Kiedy wywołuje funkcję członka w instancji zerowej skutkuje niezdefiniowanym zachowaniem?

Podczas zdecydowania wywołując niezdefiniowane zachowanie, to niezdefiniowane zachowanie prawie na pewno nie przejawia się w wypadku w konkretnym przykładzie. Powodem tego jest proste: ciało twojej lambdy nie uzyskuje dostępu do żadnych członków klasy, więc nawet jeśli operator () jest wywoływany za nullptr dla this, nie kiedykolwiek próbujesz uzyskać dostęp do dowolnej pamięci na podstawie tego wskaźnika. Rodzaj Lambda nie ma nawet żadnych członków, ponieważ Lambda nic nie przechwytuje. Ponadto, w zoptymalizowanej kompilacji, można oczekiwać, że kompilator po prostu optymalizuje się x z tego przykładowego programu, ponieważ nic o tym nic nie obserwowalne ...

Jeśli wykonasz szablon var constexpr, kompilator odmówi ich skompilowania, ponieważ wyrażenie użyte do zainicjowania zmiennej nie jest stałym wyrażeniem, ponieważ wywołuje niezdefiniowane zachowanie [Expr.Const] /2.6...

Jeśli modyfikujemy swój przykład tak, że Lambda ma członka i uzyskać dostęp do tego członka w korpusie Lambda

template<typename T>
int getvar (const T&)
{
    return (*(T*) nullptr) ();
}

int main ()
{
    return getvar ([x = 42]() { return x; });
}

Będziesz bardzo prawdopodobnie skończyć z programem, który faktycznie się rozbija ...

4
Michael Kenzel 14 grudzień 2019, 20:02