Pengurangan tipe otomatis yang tidak cocok antara berbagai kompiler c ++

10

Jadi, saya mencoba menerapkan produk titik ( https://en.wikipedia.org/wiki/Dot_product ) dalam beberapa rasa C ++ modern dan muncul dengan kode berikut:

#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 dan juga: cppinsight

Kode di atas mengkompilasi dan mengeksekusi dengan baik g++, namun clang( iccdan msvc) tersedak:

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.

Sekarang, jika saya memecah definisi v1, v2, i1, i2seperti:

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

clangdan msvctidak memiliki masalah, iccmasih tersedak:

<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

Namun jika saya menghapus kesalahan static_assertmaka icctidak ada masalah dalam mengkompilasi kode.

Dan di samping pertanyaan (khas): mana yang benar dan mengapa :) pertanyaan konkretnya adalah:

Menurut [dcl.spec.auto]:

jika tipe yang menggantikan tipe placeholder tidak sama dalam setiap deduksi, programnya salah bentuk

clangdiidentifikasi dengan benar bahwa ada dua tipe berbeda yang didefinisikan dalam baris yang dimaksud: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'jadi saya ingin mendengar pendapat Anda apakah:

Terima kasih telah membaca pertanyaan panjang ini. (Sebagai bonus jika seseorang bisa menjawab mengapa iccgagal pada static_assertakan menjadi besar.)

Ferenc Deak
sumber
1
Apa gunanya di std::forward<Args>(args)sini?
Evg
test.cpp: Dalam fungsi 'int main ()': test.cpp: 4: 5: error: deduksi yang tidak konsisten untuk 'auto': 'long int' dan kemudian 'double' 4 | otomatis i = 0l, f = 0,0; | ^ ~~~ Dengan g ++, jadi sepertinya tidak memperpanjang ini secara umum.
n314159
mencetak jenisnya memberi kita: std :: initializer_list <int>, int const * std :: initializer_list <int>, int const * di g ++, jadi ini menghasilkan tipe yang berbeda.
n314159
3
GCC tidak dapat dikompilasi auto v = { 1, 2, 3 }, i = v.begin(); . Tidak mengerti bahwa itu mengkompilasi lambda insiede yang sama. Contoh minimal: gcc.godbolt.org/z/a5XyxU . Ia bahkan mengkompilasi di dalam functor yang ditentukan pengguna: gcc.godbolt.org/z/eYutyK , atau fungsi templat: gcc.godbolt.org/z/jnEYXh .
Daniel Langr
2
@underscore_d saya kira begitu. Contoh yang sangat minimal adalah template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }, ketika dipanggil, misalnya, sebagai f(1);. Ditulis ulang sebagai void f(int a) { /* same body */ }penyebab kesalahan kompilasi.
Daniel Langr

Jawaban:

2

Memperluas dari komentar saya:

g ++ tidak selalu melakukan ini, perhatikan contohnya auto i = 0l, f = 0.0;, ia memberikan kesalahan:

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;

Jika kami mengkompilasi program Anda dan mencetak jenis variabel ( dengan metode ini ), kami mendapatkan hasil sebagai berikut:

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

menggunakan gcc versi 9.2.0, dengan bendera -std=c++17 -pedantic -Wall -Wextratanpa peringatan atau kesalahan.

Dengan komentar Anda tentang standar ini, program ini tidak berbentuk dan standar menentukan bahwa harus ada pesan diagnostik (peringatan atau kesalahan) kecuali dinyatakan sebaliknya (yang tidak, dalam hal ini). Karenanya saya akan mengatakan bahwa ini adalah bug di gcc.

Ini adalah bug yang dikenal .

n314159
sumber
Karena ini adalah bug yang sangat mudah digunakan ... beberapa orang mungkin berpendapat itu adalah fitur: D Terima kasih atas wawasan Anda!
Ferenc Deak
Akan lebih bagus jika seseorang dapat mengajukan bug terhadap g++hal ini.
underscore_d
1
Saya belum pernah melakukan itu sebelumnya tetapi saya bisa memeriksanya dalam beberapa jam.
n314159
gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 Semoga itu adalah laporan bug yang masuk akal.
n314159
0

The static_assertgagal pada ICC pasti bug. Saya menemukan solusi sederhana dengan bergerakstatic_assert ke fungsi yang terpisah. Bukan solusi yang sangat elegan, tetapi berhasil.

Dengan sedikit modifikasi, ini adalah kode yang mengkompilasi dengan GCC, Dentang dan 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
sumber
Apakah ada bug terhadap ICC untuk itu? :-)
underscore_d
Anda bilang jelas ada bug di ICC, jadi saya ingin tahu apakah mereka sudah memiliki laporan bug ini yang dikirimkan oleh seseorang. Jika tidak, ini mungkin saat yang tepat untuk membuatnya.
underscore_d
1
@underscore_d, saya belum memeriksa, tapi saya akan.
Evg