2017-12-07 121 views
7

Poniższy kod jest akceptowany przez GCC 7.2 oraz brzękiem 5.0.0, ale zostaje odrzucony przez Microsoft VS 2017 15.5.0 Podgląd 5 i Intel C++ 19:Generic lambda i jej argument jako stałej ekspresji

struct S { }; 

constexpr int f(S) 
{ 
    return 0; 
} 

int main() 
{ 
    auto lambda = [](auto x) 
    { 
     constexpr int e = f(x); 
    }; 

    lambda(S{}); 
} 

Microsoft:

<source>(12): error C2131: expression did not evaluate to a constant 

Intel:

<source>(12): error: expression must have a constant value 
    constexpr int e = f(x); 
        ^
<source>(12): note: the value of parameter "x" (declared at line 10) cannot be used as a constant 
    constexpr int e = f(x); 
         ^

Gdybym zastąpić f(x) z f(decltype(x){}), zarówno Microsoft, jak i Intel nie narzekają. Rozumiem, że x nie jest wyrażeniem stałym, ale nie jest używane wewnątrz f. Prawdopodobnie dlatego GCC i klang nie narzekają.

Podejrzewam, że kompilatory Microsoft i Intel mają rację, odrzucając ten kod. Co myślisz?

+0

@RichardHodges, '-std = C++ 14'. – Evgeny

+0

OK, teraz myślę, że jest to błąd gcc i clanga, ponieważ C++ 14 nie ma konstelpr lambda ... to jest dostępne od C++ 17 –

+0

@WF, lambda celowo nie jest "constexpr" (w prawdziwy kod to nie jest). – Evgeny

Odpowiedz

4

Od [expr.const]:

Wyrażenie e jest rdzeń stałym wyrażeniem chyba oceny e, zgodnie z zasadami abstrakcyjnej maszyny, by ocenić jedno z następujących wyrażeń:

  • [...]
  • Konwersja lwartość do RValue, chyba że stosuje się

    • nielotny glvalue integralnego lub wyliczania typu, który odnosi się do całkowitego nieulotnej obiektu const z poprzednich inicjalizacji inicjowany stałą ekspresję lub nielotną glwartość, która odnosi się do podobieństwa literału ciągu znaków, lub nielotną glwartość, która odnosi się do nielotnego obiektu zdefiniowanego za pomocą constexpr, lub która odnosi się do nie-lotnego obiektu zdefiniowanego za pomocą constexpr; -mutable podobiekt takiego obiektu, lub
    • nielotna glownośd typu literalnego, która odnosi się do nielotnego obiektu, którego żywotność rozpoczęła się w trakcie oceny e;
  • [...]

W f(x) robimy konwersję lwartość-to-RValue na x. x nie jest typu integralnego ani typu wyliczeniowego, nie jest podobiektem ciąg-literału, nie jest obiektem zdefiniowanym przez constexpr, a jego czas życia nie rozpoczyna się od oceny f(x).

Wydaje się, że nie jest to stałe wyrażenie ciągłe.

Jednak, jak Casey zwraca uwagę, ponieważ S jest pusty, nic w domyśle wygenerowanego konstruktora kopii faktycznie wywołać tę konwersję lwartość-to-RValue. Oznaczałoby to, że nic w tym wyrażeniu nie narusza żadnego z podstawowych ograniczeń stałej ekspresji, a zatem gcc i clang mają rację, akceptując je. Ta interpretacja wydaje mi się właściwa. constexpr jest zabawa.

+2

"W' f (x) ', robimy konwersję lwartość do rvalue na' x' "Myślę, że to pomyłka. Ponieważ 'S' jest puste, jego konstruktor kopiowania nigdy nie wykonuje konwersji l-do-r-wartości na źródłowej l-wartości. Kopia nie-stałego wyrażenia "S" może nadal być wyrażeniem stałym (https://godbolt.org/g/m4jSgz). 'constexpr' czasami jest szalony. – Casey

+0

@ Casey Myślę, że to właściwie odpowiednia interpretacja. W rzeczywistości szalony. – Barry

2

To nie jest błąd gcc/clang. To samo zachowanie może być powielana w C++ 11 z funkcją szablonu:

template <typename T> 
void foo(T x) 
{ 
    constexpr int e = f(x); 
} 

int main() 
{ 
    foo(S{}); 
} 

on godbolt.org


Pytanie jest, biorąc pod uwagę ...

template <typename T> 
void foo(T x) 
{ 
    constexpr int e = f(x); 
} 

... jest f(x) Stałe wyrażenie?

Od [expr.const]:

Wyrażenie e jest rdzeń stałym wyrażeniem chyba oceny e, zgodnie z zasadami abstrakcyjnej maszyny, by ocenić jedno z następujących wyrażeń:

  • wywołanie funkcji innej niż konstruktor constexpr dla klasy literalnej, funkcji constexpr lub niejawnego wywołania trywialnego destruktora

S{} i 0 są stałe wyrażenia, ponieważ nie naruszają żadnych przepisów w [expr.const]. f(x) jest wyrażeniem stałym, ponieważ jest to wywołanie funkcji constexpr.

O ile czegoś nie brakuje, gcc i clang są tutaj poprawne.

Powiązane problemy