Donc, j'essaie d'implémenter le produit scalaire ( https://en.wikipedia.org/wiki/Dot_product ) dans une certaine saveur du C ++ moderne et j'ai trouvé le code suivant:
#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;
}
En ligne: https://gcc.godbolt.org/z/kDSney et aussi: cppinsights
Le code ci-dessus se compile et s'exécute bien avec g++
, cependant clang
(et icc
et msvc
) étranglé dessus:
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.
Maintenant, si je casse la définition v1
, v2
, i1
, i2
comme:
auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();
clang
et msvc
n'ont aucun problème, icc
étouffe toujours:
<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
Toutefois , si je supprime l'infraction static_assert
alors icc
n'a aucun problème compilant soit le code.
Et à côté de la question (typique): qui est juste et pourquoi :) la question concrète est:
Selon [dcl.spec.auto]
:
si le type qui remplace le type d'espace réservé n'est pas le même dans chaque déduction, le programme est mal formé
clang
correctement identifié qu'il existe deux types différents définis dans la ligne en question: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'
je voudrais donc entendre vos opinions si:
- ai-je touché une extension g ++ non documentée compte tenu de cette situation spécifique (non mentionnée dans https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions ) depuis g ++ à ma connaissance gère correctement les différents types dans une liste de déclaration automatique,
- ou par hasard g ++ n'a pas déduit que les deux types étaient différents (... hm ...)
- ou autre chose?
Merci d'avoir lu cette longue question. (En prime, si quelqu'un pouvait répondre, pourquoi icc
échouer static_assert
serait génial.)
auto v = { 1, 2, 3 }, i = v.begin();
. Je ne comprends pas qu'il compile le même lambda insiede. Exemple minimal: gcc.godbolt.org/z/a5XyxU . Il compile même à l'intérieur d'un foncteur défini par l'utilisateur: gcc.godbolt.org/z/eYutyK , ou d'une fonction de modèle: gcc.godbolt.org/z/jnEYXh .
template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }
, lorsqu'il est invoqué, par exemple, comme f(1);
. Réécrit en tant void f(int a) { /* same body */ }
qu'erreur de compilation.
std::forward<Args>(args)
ici?