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
( icc
i 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
, i2
takich jak:
auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();
clang
i msvc
nie mam problemów, icc
nadal 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_assert
wtedy icc
nie 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
clang
poprawnie 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:
- czy trafiłem na jakieś nieudokumentowane rozszerzenie g ++, biorąc pod uwagę tę konkretną sytuację (niewymienioną w https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions ) od g ++ według mojej wiedzy poprawnie obsługuje różne typy na liście automatycznych deklaracji,
- lub przypadkiem g ++ nie wywnioskował, że oba typy są różne (... hm ...)
- albo coś innego?
Dziękuję za przeczytanie tego długiego pytania. (Jako bonus, jeśli ktoś może odpowiedzieć, dlaczego icc
nie powiedzie się, static_assert
byłoby świetnie.)
std::forward<Args>(args)
tego?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 .template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }
wywołanie npf(1);
. As . Przepisane jakovoid f(int a) { /* same body */ }
powoduje błąd kompilacji.Odpowiedzi:
Rozszerzając z moich komentarzy:
g ++ nie robi tego zawsze, rozważmy przykład
auto i = 0l, f = 0.0;
, podaje błąd:Jeśli skompilujemy Twój program i wydrukujemy typy zmiennych ( tą metodą ), otrzymamy następujące dane wyjściowe:
używając gcc w wersji 9.2.0, z flagami
-std=c++17 -pedantic -Wall -Wextra
bez ż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 .
źródło
g++
tej sprawie.static_assert
Zawieść na MTK jest zdecydowanie bug. Znalazłem proste obejście, przechodzącstatic_assert
do osobnej funkcji. Niezbyt eleganckie rozwiązanie, ale działa.Z niewielkimi modyfikacjami jest to kod, który kompiluje się z GCC, Clang i ICC:
źródło