Déduction inadéquate des types automatiques entre différents compilateurs c ++

10

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 iccet 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, i2comme:

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

clanget msvcn'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_assertalors iccn'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é

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

Merci d'avoir lu cette longue question. (En prime, si quelqu'un pouvait répondre, pourquoi iccéchouer static_assertserait génial.)

Ferenc Deak
la source
1
Quelle est l'utilité std::forward<Args>(args)ici?
Evg
test.cpp: dans la fonction 'int main ()': test.cpp: 4: 5: erreur: déduction incohérente pour 'auto': 'long int' puis 'double' 4 | auto i = 0 l, f = 0,0; | ^ ~~~ Avec g ++, il semble donc que cela ne s'étende pas en général.
n314159
l'impression des types nous donne: std :: initializer_list <int>, int const * std :: initializer_list <int>, int const * dans g ++, donc il en déduit différents types.
n314159
3
GCC ne compile pas 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 .
Daniel Langr
2
@underscore_d Je suppose que oui. L'exemple très minimal est 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.
Daniel Langr

Réponses:

2

Expansion de mes commentaires:

g ++ ne le fait pas toujours, considérez l'exemple auto i = 0l, f = 0.0;, il donne l'erreur:

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;

Si nous compilons votre programme et imprimons les types de variables ( avec cette méthode ), nous obtenons la sortie suivante:

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

en utilisant gcc version 9.2.0, avec des drapeaux -std=c++17 -pedantic -Wall -Wextrasans 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 .

n314159
la source
Puisqu'il s'agit d'un bug très pratique ... certains pourraient affirmer qu'il s'agit d'une fonctionnalité: D Merci pour vos idées!
Ferenc Deak
Ce serait formidable si quelqu'un pouvait déposer un bug contre g++à ce sujet.
underscore_d
1
Je n'avais jamais fait ça auparavant mais je peux m'y intéresser en quelques heures.
n314159
gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 J'espère que c'est un rapport de bogue judicieux.
n314159
0

L' static_assertéchec sur ICC est définitivement un bug. J'ai trouvé une solution simple en passant static_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:

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
la source
Y a-t-il un bogue contre ICC pour cela? :-)
underscore_d
Vous avez dit qu'il y avait clairement un bogue dans ICC, donc je me demande s'ils avaient déjà un rapport de ce bogue soumis par quelqu'un. Sinon, ce pourrait être le bon moment pour en créer un.
underscore_d
1
@underscore_d, je n'ai pas encore vérifié, mais je le ferai.
Evg