Daher versuche ich, das Punktprodukt ( https://en.wikipedia.org/wiki/Dot_product ) in einer Variante des modernen C ++ zu implementieren, und habe den folgenden Code gefunden:
#include <iostream>
template<class... Args>
auto dot(Args... args)
{
auto a = [args...](Args...)
{
return [=](auto... brgs)
{
static_assert(sizeof...(args) == sizeof...(brgs));
auto v1 = {args...}, i1 = v1.begin();
auto v2 = {brgs...}, i2 = v2.begin();
typename std::common_type<Args...>::type s = 0;
while( i1 != v1.end() && i2!= v2.end())
{
s += *i1++ * *i2++;
}
return s;
};
};
return a(std::forward<Args>(args)...);
}
int main()
{
auto a = dot(1,3,-5)(4,-2,-1);
std::cout << a << std::endl;
}
Online: https://gcc.godbolt.org/z/kDSney und auch: cppinsights
Der obige Code kompiliert und wird gut ausgeführt g++
, jedoch clang
(und icc
und msvc
) ersticken daran:
clang++ ./funcpp.cpp --std=c++17
./funcpp.cpp:12:4: error: 'auto' deduced as 'std::initializer_list<int>' in declaration of
'v1' and deduced as 'const int *' in declaration of 'i1'
auto v1 = {args...}, i1 = v1.begin();
^ ~~~~~~~~~ ~~~~~~~~~~
./funcpp.cpp:28:11: note: in instantiation of function template specialization
'dot<int, int, int>' requested here
auto a = dot(1,3,-5)(4,-2,-1);
^
1 error generated.
Nun, wenn ich breche die Definition von v1
, v2
, i1
, i2
wie:
auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();
clang
und msvc
habe keine probleme, icc
erstickt immer noch:
<source>(10): error: static assertion failed
static_assert(sizeof...(args) == sizeof...(brgs));
^
detected during instantiation of "auto dot(Args...) [with Args=<int, int, int>]" at line 30
compilation aborted for <source> (code 2)
Execution build compiler returned: 2
Allerdings , wenn ich die fehlerhafte entfernen static_assert
dann icc
hat keine Probleme den Code entweder kompilieren.
Und neben der (typischen) Frage: Was ist richtig und warum :) lautet die konkrete Frage:
Laut [dcl.spec.auto]
:
Wenn der Typ, der den Platzhaltertyp ersetzt, nicht bei jedem Abzug gleich ist, ist das Programm fehlerhaft
clang
richtig identifiziert, dass in der fraglichen Zeile zwei verschiedene Typen definiert sind: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'
Ich würde gerne Ihre Meinung hören, ob:
- Habe ich in Anbetracht dieser speziellen Situation eine undokumentierte g ++ - Erweiterung getroffen (nicht in https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions erwähnt ), seit g ++ meines Wissens behandelt die verschiedenen Typen in einer automatischen Deklarationsliste korrekt.
- oder zufällig hat g ++ nicht abgeleitet, dass die beiden Typen unterschiedlich sind (... hm ...)
- oder etwas anderes?
Vielen Dank, dass Sie diese lange Frage gelesen haben. (Als Bonus wäre es großartig, wenn jemand antworten könnte, warum das icc
fehlschlägt static_assert
.)
std::forward<Args>(args)
hier?auto v = { 1, 2, 3 }, i = v.begin();
. Verstehe nicht, dass es das gleiche Insiede Lambda kompiliert. Minimales Beispiel: gcc.godbolt.org/z/a5XyxU . Es wird sogar in einem benutzerdefinierten Funktor kompiliert: gcc.godbolt.org/z/eYutyK oder einer Vorlagenfunktion: gcc.godbolt.org/z/jnEYXh .template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }
, wenn es aufgerufen wird, zf(1);
. B. as . Umgeschrieben alsvoid f(int a) { /* same body */ }
verursacht Kompilierungsfehler.Antworten:
Erweitern von meinen Kommentaren:
g ++ macht das nicht immer, betrachten Sie das Beispiel
auto i = 0l, f = 0.0;
, es gibt den Fehler:Wenn wir Ihr Programm kompilieren und die Typen der Variablen drucken ( mit dieser Methode ), erhalten wir die folgende Ausgabe:
Verwenden von gcc Version 9.2.0 mit Flags
-std=c++17 -pedantic -Wall -Wextra
ohne Warnung oder Fehler.Durch Ihren Kommentar zum Standard ist dieses Programm fehlerhaft und der Standard legt fest, dass eine Diagnosemeldung (Warnung oder Fehler) ausgegeben werden soll, sofern nicht anders angegeben (was in diesem Fall nicht der Fall ist). Daher würde ich sagen, dass dies ein Fehler in gcc ist.
Es ist ein bekannter Fehler .
quelle
g++
.Der
static_assert
Fehler bei ICC ist definitiv ein Fehler. Ich habe eine einfache Problemumgehung gefunden, indem ichstatic_assert
in eine separate Funktion gewechselt bin . Keine sehr elegante Lösung, aber es funktioniert.Mit geringfügigen Änderungen ist dies der Code, der mit GCC, Clang und ICC kompiliert wird:
quelle