wake-up-neo.net

constexpr-Funktion mit nicht verwendetem Referenzargument - gcc vs clang

Betrachten Sie den folgenden Code:

template <int N, typename T> void f(T) { }

template <typename T> 
constexpr int k(T&) { return 0; }

int main() 
{
    constexpr auto i = 1;
    f<k(i)>([&i]
    {
         f<k(i)>(0); 
    });
}

clang++(trunk) kompiliert es. g++(trunk) schlägt mit dem folgenden Fehler fehl:

<source>: In lambda function:

<source>:11:19: error: no matching function for call to 'f<k<const int>((* & i))>(int)'
11  |          f<k(i)>(0);
    |                   ^

<source>:1:35: note: candidate: 'template<int N, class T> void f(T)'
    1 | template <int N, typename T> void f(T) { }
      |                                   ^

<source>:1:35: note:   template argument deduction/substitution failed:

<source>:11:19: error: '__closure' is not a constant expression
11  |          f<k(i)>(0);
    |                   ^

<source>:11:13: note: in template argument for type 'int'
11  |          f<k(i)>(0);
    |            ~^~~

Live-Beispiel auf godbolt.org


Durch Ändern von k(T&) in k(T) wird das Problem gelöst. Es scheint mir, dass das Problem damit zusammenhängt, dass das Referenzargument kein konstanter Ausdruck ist, aber nicht als Teil von k verwendet wird.

Welcher Compiler ist hier korrekt?

7
Vittorio Romeo

GCC ist hier richtig.

Nach [expr.const]/4 :

Ein Ausdruck e ist ein Ausdruck der Kernkonstante, sofern nicht die Auswertung von e würde nach den Regeln der abstrakten Maschine auswerten einer der folgenden Ausdrücke:

  • ...
  • in einem lambda-Ausdruck eine Referenz auf eine Variable [...] mit automatischer Speicherdauer, die außerhalb des lambda-Ausdrucks, .__ definiert ist. wo der Hinweis eine odr-Verwendung wäre; ...
  • ...

k(i) odr-use i Daher ist k(i) kein konstanter Ausdruck im Lambda-Ausdruck, daher ist dieser Code schlecht geformt.

5
xskxzr

Der Fehler wird für den Ausdruck k(i) ausgegeben, der in den zusammengesetzten Anweisungen des Lambda-Ausdrucks angezeigt wird, jedoch nicht außerhalb davon. Dies ist ein GCC-Fehler. Nach [exprim.lambda.capture]/11

Ein id-expression innerhalb der zusammengesetzten Anweisung eines Lambda-Ausdrucks, bei dem es sich um eine Odr-Verwendung einer Referenz handelt, die von der Referenz bezieht sich auf die Entität, an die die erfasste Referenz gebunden ist und nicht auf die erfasste Referenz.

Also ist k(i) außerhalb des Lambda derselbe Ausdruck wie k(i) außerhalb des Lambda. Daher gibt es für GCC keinen Grund, einen Fehler für den zweiten Ausdruck auszugeben, nicht jedoch für den ersten.

0
Oliv