Nicht übereinstimmender Abzug von Autotypen zwischen verschiedenen C ++ - Compilern

10

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 iccund 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, i2wie:

auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();

clangund msvchabe keine probleme, iccerstickt 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_assertdann icchat 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

clangrichtig 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:

Vielen Dank, dass Sie diese lange Frage gelesen haben. (Als Bonus wäre es großartig, wenn jemand antworten könnte, warum das iccfehlschlägt static_assert.)

Ferenc Deak
quelle
1
Was nützt das std::forward<Args>(args)hier?
Evg
test.cpp: In der Funktion 'int main ()': test.cpp: 4: 5: Fehler: inkonsistenter Abzug für 'auto': 'long int' und dann 'double' 4 | Auto i = 0l, f = 0,0; | ^ ~~~ Mit g ++ scheint es dies im Allgemeinen nicht zu erweitern.
n314159
Wenn wir die Typen drucken, erhalten wir: std :: initializer_list <int>, int const * std :: initializer_list <int>, int const * in g ++, sodass verschiedene Typen abgeleitet werden.
n314159
3
GCC wird nicht kompiliert 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 .
Daniel Langr
2
@underscore_d Ich denke schon. Das sehr minimale Beispiel ist template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }, wenn es aufgerufen wird, z f(1);. B. as . Umgeschrieben als void f(int a) { /* same body */ }verursacht Kompilierungsfehler.
Daniel Langr

Antworten:

2

Erweitern von meinen Kommentaren:

g ++ macht das nicht immer, betrachten Sie das Beispiel auto i = 0l, f = 0.0;, es gibt den Fehler:

test.cpp: In function int main()’:
test.cpp:4:5: error: inconsistent deduction for auto’: long int and then double
    4 |     auto i = 0l, f = 0.0;

Wenn wir Ihr Programm kompilieren und die Typen der Variablen drucken ( mit dieser Methode ), erhalten wir die folgende Ausgabe:

v1: std::initializer_list<int>, i1: int const*
v2: std::initializer_list<int>, i2: int const*

Verwenden von gcc Version 9.2.0 mit Flags -std=c++17 -pedantic -Wall -Wextraohne 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 .

n314159
quelle
Da es sich um einen sehr praktischen Fehler handelt, könnten einige argumentieren, dass es sich um eine Funktion handelt: D Vielen Dank für Ihre Erkenntnisse!
Ferenc Deak
Es wäre großartig, wenn jemand einen Fehler dagegen einreichen könnte g++.
underscore_d
1
Ich habe das noch nie gemacht, aber ich kann es in ein paar Stunden untersuchen.
n314159
gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 Ich hoffe, das ist ein vernünftiger Fehlerbericht.
n314159
0

Der static_assertFehler bei ICC ist definitiv ein Fehler. Ich habe eine einfache Problemumgehung gefunden, indem ich static_assertin 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:

template<std::size_t size, class... Args>
void args_no_guard(Args... args)
{
    static_assert(sizeof...(args) == size);
}

template<class... Args>
auto dot(Args... args)
{
    return [=](auto... brgs)
    {
        constexpr auto n = sizeof...(args);
        args_no_guard<n>(brgs...);

        using T = std::common_type_t<decltype(args)..., decltype(brgs)...>;
        const T v1[]{static_cast<T>(args)...};
        const T v2[]{static_cast<T>(brgs)...};

        T dot = 0;
        for (std::size_t i = 0; i < n; ++i)
            dot += v1[i] * v2[i];
        return dot;
    };
}
Evg
quelle
Gibt es dafür einen Fehler gegen ICC? :-)
underscore_d
Sie sagten, es gibt eindeutig einen Fehler in ICC, also frage ich mich, ob sie bereits einen Bericht über diesen Fehler von jemandem eingereicht haben. Wenn nicht, ist dies möglicherweise ein guter Zeitpunkt, um eine zu erstellen.
underscore_d
1
@underscore_d, ich habe noch nicht überprüft, aber ich werde.
Evg