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.)
std::forward<Args>(args)
ici?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, commef(1);
. Réécrit en tantvoid f(int a) { /* same body */ }
qu'erreur de compilation.Réponses:
Expansion de mes commentaires:
g ++ ne le fait pas toujours, considérez l'exemple
auto i = 0l, f = 0.0;
, il donne l'erreur:Si nous compilons votre programme et imprimons les types de variables ( avec cette méthode ), nous obtenons la sortie suivante:
en utilisant gcc version 9.2.0, avec des drapeaux
-std=c++17 -pedantic -Wall -Wextra
sans avertissement ni erreur.Par votre commentaire de la norme, ce programme est mal formé et la norme spécifie qu'il devrait y avoir un message de diagnostic (avertissement ou erreur) sauf indication contraire (ce qui n'est pas le cas dans ce cas). Par conséquent, je dirais que c'est un bogue dans gcc.
Il s'agit d' un bug connu .
la source
g++
à ce sujet.L'
static_assert
échec sur ICC est définitivement un bug. J'ai trouvé une solution simple en passantstatic_assert
à une fonction distincte. Pas une solution très élégante, mais ça marche.Avec de légères modifications, voici le code qui se compile avec GCC, Clang et ICC:
la source