10 votes

Déduction non conforme des types auto entre différents compilateurs C++

J'essaie donc de mettre en œuvre le produit de points ( https://en.wikipedia.org/wiki/Dot_product ) dans un certain type de C++ moderne et j'ai obtenu 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 correctement avec g++ Cependant clang (et icc y msvc ) s'en étouffent :

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 décompose la définition de v1 , v2 , i1 , i2 comme :

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

clang y msvc n'ont aucun problème, icc s'étouffe encore :

<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 le static_assert puis icc n'a pas non plus de problème pour compiler le code.

Outre la question (typique) de savoir qui a raison et pourquoi :), la question concrète est la suivante :

Selon le [dcl.spec.auto] :

si le type qui remplace le type de l'espace réservé n'est pas le même dans chaque déduction, le programme est mal formé

clang a 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' J'aimerais donc connaître votre opinion à ce sujet :

Merci d'avoir lu cette longue question. (En prime, si quelqu'un peut répondre à la question de savoir pourquoi icc échoue sur le static_assert serait une excellente chose).

2voto

n314159 Points 4691

Je développe mes commentaires :

g++ ne le fait pas toujours, voir l'exemple suivant 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 que nous imprimons les types des variables ( avec cette méthode ), nous obtenons le résultat suivant :

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

en utilisant la version 9.2.0 de gcc, avec les drapeaux -std=c++17 -pedantic -Wall -Wextra sans avertissement ni erreur.

D'après votre commentaire de la norme, ce programme est mal formé et la norme précise qu'un message de diagnostic (avertissement ou erreur) doit être émis, sauf indication contraire (ce qui n'est pas le cas ici). Je dirais donc qu'il s'agit d'un bogue dans gcc.

Il est un bogue connu .

0voto

Evg Points 4011

En static_assert L'échec de la CPI est certainement un bogue. J'ai trouvé une solution simple en déplaçant static_assert dans une fonction distincte. Cette solution n'est pas très élégante, mais elle fonctionne.

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;
    };
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X