Kode berikut mengkompilasi baik-baik saja dengan dentang-trunk dalam mode c ++ 17 tetapi istirahat dalam mode c ++ 2a (mendatang c ++ 20):
// Meta struct describing the result of a comparison
struct Meta {};
struct Foo {
Meta operator==(const Foo&) {return Meta{};}
Meta operator!=(const Foo&) {return Meta{};}
};
int main()
{
Meta res = (Foo{} != Foo{});
}
Itu juga mengkompilasi dengan gcc-trunk atau clang-9.0.0: https://godbolt.org/z/8GGT78
Kesalahan dengan dentang-batang dan -std=c++2a
:
<source>:12:19: error: use of overloaded operator '!=' is ambiguous (with operand types 'Foo' and 'Foo')
Meta res = (f != g);
~ ^ ~
<source>:6:10: note: candidate function
Meta operator!=(const Foo&) {return Meta{};}
^
<source>:5:10: note: candidate function
Meta operator==(const Foo&) {return Meta{};}
^
<source>:5:10: note: candidate function (with reversed parameter order)
Saya mengerti bahwa C + + 20 akan memungkinkan untuk hanya kelebihan operator==
dan kompiler akan secara otomatis menghasilkan operator!=
dengan meniadakan hasil operator==
. Sejauh yang saya mengerti, ini hanya berfungsi selama tipe kembalinya bool
.
Sumber masalahnya adalah bahwa di Eigen kita mendeklarasikan satu set operator ==
, !=
, <
, ... antara Array
objek atau Array
dan skalar, yang kembali (ekspresi) Array bool
(yang kemudian dapat diakses elemen-bijaksana, atau digunakan jika ). Misalnya,
#include <Eigen/Core>
int main()
{
Eigen::ArrayXd a(10);
a.setRandom();
return (a != 0.0).any();
}
Berbeda dengan contoh saya di atas ini bahkan gagal dengan gcc-trunk: https://godbolt.org/z/RWktKs . Saya belum berhasil menguranginya menjadi contoh non-Eigen, yang gagal di dentang-trunk dan gcc-trunk (contoh di atas cukup disederhanakan).
Laporan masalah terkait: https://gitlab.com/libeigen/eigen/issues/1833
Pertanyaan saya yang sebenarnya: Apakah ini benar-benar perubahan besar pada C ++ 20 (dan apakah ada kemungkinan untuk membebani operator pembanding untuk mengembalikan Meta-objek), atau apakah itu lebih cenderung berupa regresi dalam dentang / gcc?
Jawaban:
Masalah Eigen tampaknya mengurangi sebagai berikut:
Dua kandidat untuk berekspresi adalah
operator==(const Scalar&, const Derived&)
Base<X>::operator!=(const Scalar&) const
Per [over.match.funcs] / 4 , seperti
operator!=
yang tidak diimpor ke dalam lingkupX
dengan menggunakan-deklarasi , jenis parameter objek implisit untuk # 2 adalahconst Base<X>&
. Akibatnya, # 1 memiliki urutan konversi implisit yang lebih baik untuk argumen itu (kecocokan persis, daripada konversi yang diturunkan ke basis). Memilih # 1 kemudian menjadikan program tersebut salah bentuk.Kemungkinan perbaikan:
using Base::operator!=;
keDerived
, atauoperator==
untuk mengambilconst Base&
alih - alih aconst Derived&
.sumber
bool
dari merekaoperator==
? Karena itu tampaknya menjadi satu-satunya alasan kode ini dibuat dengan buruk di bawah aturan baru.operator==(Array, Scalar)
yang melakukan perbandingan elemen-bijaksana dan mengembalikanArray
daribool
. Anda tidak dapat mengubahnya menjadibool
tanpa merusak yang lainnya.operator==
tidak seharusnya memengaruhi kode yang ada, namun mereka melakukannya dalam kasus ini, karena pemeriksaan untukbool
nilai pengembalian bukan bagian dari pemilihan kandidat untuk penulisan ulang.Ya, sebenarnya kode itu istirahat di C ++ 20.
Ekspresi
Foo{} != Foo{}
memiliki tiga kandidat dalam C ++ 20 (sedangkan hanya ada satu di C ++ 17):Ini berasal dari aturan kandidat yang ditulis ulang di [over.match.oper] /3.4 . Semua kandidat itu layak, karena
Foo
argumen kami tidakconst
. Untuk menemukan kandidat yang terbaik, kita harus melalui tiebreak kita.Aturan yang relevan untuk fungsi terbaik adalah dari [over.match.best] / 2 :
#2
dan#3
merupakan kandidat yang ditulis ulang, dan#3
telah membalik urutan parameter, sementara#1
tidak ditulis ulang. Tetapi untuk mencapai tiebreak itu, kita harus terlebih dahulu melewati kondisi awal itu: untuk semua argumen urutan konversi tidak lebih buruk.#1
lebih baik daripada#2
karena semua urutan konversi adalah sama (sepele, karena parameter fungsi adalah sama) dan#2
merupakan kandidat yang ditulis ulang sementara#1
tidak.Tapi ... keduanya berpasangan
#1
/#3
dan#2
/#3
terjebak pada kondisi pertama itu. Dalam kedua kasus, parameter pertama memiliki urutan konversi yang lebih baik untuk#1
/#2
sedangkan parameter kedua memiliki urutan konversi yang lebih baik untuk#3
(parameter yangconst
harus menjalaniconst
kualifikasi tambahan , sehingga memiliki urutan konversi yang lebih buruk). Iniconst
flip-flop menyebabkan kita tidak dapat memilih satu baik.Sebagai hasilnya, resolusi overload keseluruhan ambigu.
Itu tidak benar. Kami tanpa syarat mempertimbangkan kandidat yang ditulis ulang dan dibalik. Aturan yang kita miliki adalah, dari [over.match.oper] / 9 :
Artinya, kami masih mempertimbangkan kandidat ini. Tetapi jika kandidat terbaik yang layak adalah
operator==
yang mengembalikan, katakanlah,Meta
- hasilnya pada dasarnya sama dengan jika kandidat itu dihapus.Kami tidak ingin berada dalam keadaan di mana resolusi kelebihan harus mempertimbangkan jenis kembali. Dan dalam hal apapun, fakta bahwa kode di sini kembali
Meta
tidak penting - masalahnya juga akan ada jika dikembalikanbool
.Untungnya, perbaikannya di sini mudah:
Setelah Anda membuat kedua operator pembanding
const
, tidak ada lagi ambiguitas. Semua parameter sama, sehingga semua urutan konversi sepele sama.#1
sekarang akan dikalahkan#3
oleh bukan oleh ditulis ulang dan#2
sekarang akan dikalahkan#3
dengan tidak terbalik - yang membuat#1
kandidat terbaik. Hasil yang sama dengan yang kami miliki di C ++ 17, hanya beberapa langkah lagi untuk sampai ke sana.sumber
==
dan jenis kembalinya fungsi yang dipilih tidakbool
. Tetapi pemusnahan ini tidak terjadi selama resolusi kelebihan itu sendiri.cv bool
(dan sebelum perubahan ini, persyaratannya adalah konversi kontekstual kebool
- masih belum!
)[over.match.best] / 2 mencantumkan prioritas overload yang valid dalam satu set. Bagian 2.8 memberi tahu kita bahwa
F1
itu lebih baik daripadaF2
jika (di antara banyak hal lain):Contoh di sana menunjukkan panggilan eksplisit
operator<
meskipunoperator<=>
ada di sana.Dan [over.match.oper] /3.4.3 memberi tahu kita bahwa pencalonan
operator==
dalam keadaan ini adalah kandidat yang ditulis ulang.Namun , operator Anda lupa satu hal penting: mereka harus
const
berfungsi. Dan membuatnya tidakconst
menyebabkan aspek-aspek sebelumnya dari resolusi kelebihan beban ikut bermain. Fungsi tidak adalah sama persis, sebagai nonconst
-to-const
konversi perlu terjadi untuk argumen yang berbeda. Itu menyebabkan ambiguitas yang dimaksud.Setelah Anda membuatnya
const
, dentang kompilasi batang .Saya tidak dapat berbicara dengan Eigen yang lain, karena saya tidak tahu kodenya, ini sangat besar, dan karenanya tidak dapat ditampung dalam MCVE.
sumber
const
, kandidat yang tidak terbalik memiliki urutan konversi yang lebih baik untuk argumen kedua dan kandidat yang terbalik memiliki urutan konversi yang lebih baik untuk argumen pertama.const
dalam contoh minimal. Saya cukup yakin bahwa Eigen menggunakan diconst
mana-mana (atau di luar definisi kelas, juga denganconst
referensi), tetapi saya perlu memeriksa. Saya mencoba menjabarkan keseluruhan mekanisme yang digunakan Eigen ke contoh minimal, ketika saya menemukan waktu.Kami memiliki masalah serupa dengan file header Goopax kami. Kompilasi yang berikut dengan dentang-10 dan -std = c ++ 2a menghasilkan kesalahan kompiler.
Menyediakan operator tambahan ini sepertinya dapat menyelesaikan masalah:
sumber
a == 0
dikompilasi ?gpu_bool gpu_type<T>::operator==(T a) const;
dangpu_bool gpu_type<T>::operator!=(T a) const;
Dengan C ++ - 17, ini berfungsi dengan baik. Tapi sekarang dengan dentang-10 dan C ++ - 20, ini tidak ditemukan lagi, dan sebagai gantinya kompiler mencoba untuk menghasilkan operator sendiri dengan menukar argumen, dan gagal, karena tipe kembalinya tidakbool
.