Deduzione non corrispondente dei tipi automatici tra diversi compilatori c ++


10

Quindi, sto cercando di implementare il prodotto dot ( https://en.wikipedia.org/wiki/Dot_product ) in un certo stile del moderno C ++ e ho trovato il seguente codice:

#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 e anche: cppinsights

Il codice sopra si compila ed esegue bene con g++, tuttavia clang( icce msvc) soffocare su di esso:

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.

Ora, se rompo la definizione di v1, v2, i1, i2come:

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

clange msvcnon ho problemi, iccsoffoca ancora:

<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

Tuttavia se mi tolgo l'incriminato static_assertquindi iccnon ha problemi di compilazione del codice sia.

E accanto alla (tipica) domanda: quale è giusto e perché :) la domanda concreta è:

Secondo [dcl.spec.auto]:

se il tipo che sostituisce il tipo di segnaposto non è lo stesso in ogni detrazione, il programma è mal formato

clangcorrettamente identificato che ci sono due diversi tipi definiti nella riga in questione: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'quindi vorrei sentire le tue opinioni se:

Grazie per aver letto questa lunga domanda. (Come bonus se qualcuno potesse rispondere perché iccfallire su static_assertsarebbe fantastico.)


1
A che serve std::forward<Args>(args)qui?
Evg

test.cpp: nella funzione 'int main ()': test.cpp: 4: 5: errore: deduzione incoerente per 'auto': 'long int' e quindi 'double' 4 | auto i = 0l, f = 0.0; | ^ ~~~ Con g ++, quindi sembra che non lo estenda in generale.
n314159

la stampa dei tipi ci dà: std :: initializer_list <int>, int const * std :: initializer_list <int>, int const * in g ++, quindi deduce diversi tipi.
n314159

3
GCC non viene compilato auto v = { 1, 2, 3 }, i = v.begin(); . Non capisco che compila lo stesso insiede lambda. Esempio minimo: gcc.godbolt.org/z/a5XyxU . Si compila anche all'interno di un funzione definita dall'utente: gcc.godbolt.org/z/eYutyK , o una funzione modello: gcc.godbolt.org/z/jnEYXh .
Daniel Langr,

2
@underscore_d Suppongo di si. L'esempio molto minimale è template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }, quando invocato, ad esempio, come f(1);. Riscritto come void f(int a) { /* same body */ }causa errore di compilazione.
Daniel Langr,

Risposte:


2

Espandendo dai miei commenti:

g ++ non lo fa sempre, considera l'esempio auto i = 0l, f = 0.0;, dà l'errore:

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;

Se compiliamo il tuo programma e stampiamo i tipi di variabili ( con questo metodo ), otteniamo il seguente output:

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

usando gcc versione 9.2.0, con flag -std=c++17 -pedantic -Wall -Wextrasenza alcun avviso o errore.

Con il tuo commento alla norma, questo programma è mal formato e la norma specifica che dovrebbe essere emesso un messaggio diagnostico (avviso o errore) se non diversamente specificato (cosa che non è, in questo caso). Quindi direi che questo è un bug in gcc.

È un bug noto .


Dal momento che è un bug molto conveniente ... alcuni potrebbero obiettare che sia una caratteristica: D Grazie per le tue intuizioni!
Ferenc Deak,

Sarebbe bello se qualcuno potesse presentare un bug g++per questo.
underscore_d

1
Non l'ho mai fatto prima, ma posso esaminarlo in poche ore.
n314159

gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 Spero che sia una segnalazione di bug sensata.
1415159,

0

Il static_assertfallimento di ICC è sicuramente un bug. Ho trovato una soluzione semplice passando static_asserta una funzione separata. Soluzione non molto elegante, ma funziona.

Con lievi modifiche, questo è il codice che viene compilato con GCC, Clang e 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;
    };
}

C'è un bug contro ICC per questo? :-)
underscore_d

Hai detto che c'è chiaramente un bug in ICC, quindi mi chiedo se avevano già segnalato un bug di questo bug da qualcuno. In caso contrario, potrebbe essere un buon momento per crearne uno.
underscore_d

1
@underscore_d, non ho ancora verificato, ma lo farò.
Evg
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.