Niedopasowane odliczanie typów auto między różnymi kompilatorami c ++

10

Próbuję więc wdrożyć produkt kropkowy ( https://en.wikipedia.org/wiki/Dot_product ) w jakimś smaku współczesnego C ++ i wymyśliłem następujący kod:

#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, a także: cppinsights

Powyższy kod ładnie się kompiluje i wykonuje g++, jednak clang( icci msvc) dusi się na nim:

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.

Teraz, jeśli zerwać definicję v1, v2, i1, i2takich jak:

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

clangi msvcnie mam problemów, iccnadal dusi się:

<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

Jednak jeśli usunąć przestępstwa static_assertwtedy iccnie ma problemów kompilacji kodu albo.

A poza (typowym) pytaniem: które jest słuszne i dlaczego :) konkretnym pytaniem jest:

Według [dcl.spec.auto]:

jeśli typ, który zastępuje typ zastępczy, nie jest taki sam w każdej dedukcji, program jest źle sformułowany

clangpoprawnie zidentyfikowano, że w tym wierszu zdefiniowano dwa różne typy: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'więc chciałbym usłyszeć twoje opinie, czy:

Dziękuję za przeczytanie tego długiego pytania. (Jako bonus, jeśli ktoś może odpowiedzieć, dlaczego iccnie powiedzie się, static_assertbyłoby świetnie.)

Ferenc Deak
źródło
1
Jaki jest pożytek z std::forward<Args>(args)tego?
Evg
test.cpp: W funkcji „int main ()”: test.cpp: 4: 5: error: niespójne odliczenie dla „auto”: „long int”, a następnie „double” 4 | auto i = 0 l, f = 0,0; | ^ ~~~ Z g ++, więc wydaje się, że nie rozszerza tego w ogóle.
n314159
wypisanie typów daje nam: std :: initializer_list <int>, int const * std :: initializer_list <int>, int const * w g ++, więc dedukuje różne typy.
n314159,
3
GCC nie kompiluje się auto v = { 1, 2, 3 }, i = v.begin(); . Nie rozumiem, że kompiluje to samo insiede lambda. Minimalny przykład: gcc.godbolt.org/z/a5XyxU . Kompiluje się nawet w funktorze zdefiniowanym przez użytkownika: gcc.godbolt.org/z/eYutyK lub w funkcji szablonu: gcc.godbolt.org/z/jnEYXh .
Daniel Langr
2
@underscore_d Chyba tak. Bardzo minimalnym przykładem jest template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }wywołanie np f(1);. As . Przepisane jako void f(int a) { /* same body */ }powoduje błąd kompilacji.
Daniel Langr

Odpowiedzi:

2

Rozszerzając z moich komentarzy:

g ++ nie robi tego zawsze, rozważmy przykład auto i = 0l, f = 0.0;, podaje błąd:

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;

Jeśli skompilujemy Twój program i wydrukujemy typy zmiennych ( tą metodą ), otrzymamy następujące dane wyjściowe:

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

używając gcc w wersji 9.2.0, z flagami -std=c++17 -pedantic -Wall -Wextrabez żadnego ostrzeżenia lub błędu.

W komentarzu do standardu ten program jest źle sformułowany, a standard określa , że powinien zostać wysłany komunikat diagnostyczny (ostrzeżenie lub błąd), chyba że określono inaczej (w innym przypadku tak nie jest). Dlatego powiedziałbym, że jest to błąd w gcc.

To znany błąd .

n314159
źródło
Ponieważ jest to bardzo wygodny błąd ... niektórzy mogą twierdzić, że jest to funkcja: D Dziękujemy za wgląd!
Ferenc Deak,
Byłoby wspaniale, gdyby ktoś mógł zgłosić błąd w g++tej sprawie.
underscore_d
1
Nigdy wcześniej tego nie robiłem, ale mogę zajrzeć za kilka godzin.
n314159
gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 Mam nadzieję, że to rozsądny raport o błędzie.
n314159
0

static_assertZawieść na MTK jest zdecydowanie bug. Znalazłem proste obejście, przechodząc static_assertdo osobnej funkcji. Niezbyt eleganckie rozwiązanie, ale działa.

Z niewielkimi modyfikacjami jest to kod, który kompiluje się z GCC, Clang i ICC:

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
źródło
Czy jest w tym błąd w ICC? :-)
underscore_d
Powiedziałeś, że jest wyraźny błąd w ICC, więc zastanawiam się, czy ktoś już zgłosił ten błąd. Jeśli nie, może to być dobry moment na jego utworzenie.
underscore_d
1
@underscore_d, jeszcze nie sprawdziłem, ale zrobię to.
Evg