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
( icc
dan 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
, i2
seperti:
auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();
clang
dan msvc
tidak memiliki masalah, icc
masih 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_assert
maka icc
tidak 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
clang
diidentifikasi 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:
- apakah saya menekan beberapa ekstensi g ++ yang tidak berdokumen dengan mempertimbangkan situasi khusus ini (tidak disebutkan dalam https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions ) karena g ++ sepengetahuan saya dengan benar menangani berbagai jenis dalam daftar deklarasi otomatis,
- atau kebetulan g ++ tidak menyimpulkan dua tipe berbeda (... hm ...)
- atau sesuatu yang lain?
Terima kasih telah membaca pertanyaan panjang ini. (Sebagai bonus jika seseorang bisa menjawab mengapa icc
gagal pada static_assert
akan menjadi besar.)
std::forward<Args>(args)
sini?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 .template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }
, ketika dipanggil, misalnya, sebagaif(1);
. Ditulis ulang sebagaivoid f(int a) { /* same body */ }
penyebab kesalahan kompilasi.Jawaban:
Memperluas dari komentar saya:
g ++ tidak selalu melakukan ini, perhatikan contohnya
auto i = 0l, f = 0.0;
, ia memberikan kesalahan:Jika kami mengkompilasi program Anda dan mencetak jenis variabel ( dengan metode ini ), kami mendapatkan hasil sebagai berikut:
menggunakan gcc versi 9.2.0, dengan bendera
-std=c++17 -pedantic -Wall -Wextra
tanpa 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 .
sumber
g++
hal ini.The
static_assert
gagal 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:
sumber