Operator anggota yang salah dikurangkan dari operator global yang benar

Saya memiliki kelas 3x3 Matrix dan 3x1 Vector. Saya memiliki dua operator perkalian; satu untuk mengalikan matriks dengan skalar, satu lagi untuk mengalikan matriks dengan objek vektor. Operator perkalian matriks-skalar adalah anggota dalam kelas Matrix dan operator perkalian matriks-vektor bersifat global.

#include <initializer_list>
#include <array>

template <class T>
class Matrix
{
    public:
        Matrix(std::initializer_list<T> List);
        Matrix() : Matrix({0,0,0,0,0,0,0,0,0}) {}

        template <class S>                      // THE COMPILER TRIES TO USE
        Matrix<T> operator*(const S & Scalar);  // THIS OPERATOR IN BOTH CASES.

        const T & operator()(size_t i, size_t j) const;

    private:
        static constexpr size_t SIZE = 3;
        static constexpr size_t AREA = SIZE * SIZE;
        std::array<T, AREA> E;
};

template <class T>
Matrix<T>::Matrix(std::initializer_list<T> List)
{
    if (List.size() != AREA) throw("Error!");
    for (size_t i=0; i<AREA; i++)
    {
        E[i] = *(List.begin() + i);
    }
}

template <class T>
const T & Matrix<T>::operator()(size_t i, size_t j) const
{
    return E[SIZE * j + i];
}

template <class T>
template <class S>
Matrix<T> Matrix<T>::operator*(const S & Scalar)
{
    const T ScalarT = static_cast<T>(Scalar);
    Matrix<T> Result;
    for (size_t i=0; i<AREA; i++)
    {
        Result.E[i] = E[i] * ScalarT;
    }
    return Result;
}

template <class T>
class Vector
{
    public:
        Vector(std::initializer_list<T> List);
        Vector() : Vector({0,0,0}) {};
        const T & operator()(size_t i) const;
              T & operator()(size_t i);

    private:
        static constexpr size_t SIZE = 3;
        std::array<T, SIZE> E;
};

template <class T>
Vector<T>::Vector(std::initializer_list<T> List)
{
    if (List.size() != SIZE) throw("Error!");
    for (size_t i=0; i<SIZE; i++)
    {
        E[i] = *(List.begin() + i);
    }
}

template <class T>
const T & Vector<T>::operator()(size_t i) const
{
    return E[i];
}

template <class T>
T & Vector<T>::operator()(size_t i)
{
    return E[i];
}

template <class T>  // THE COMPILER NEVER TRIES USING THIS GLOBAL OPERATOR.
Vector<T> operator*(const Matrix<T> & Mat, const Vector<T> & Vec)
{
    Vector<T> Result;
    Result(0) = Mat(0,0) * Vec(0) + Mat(0,1) * Vec(1) + Mat(0,2) * Vec(2);
    Result(1) = Mat(1,0) * Vec(0) + Mat(1,1) * Vec(1) + Mat(1,2) * Vec(2);
    Result(2) = Mat(2,0) * Vec(0) + Mat(2,1) * Vec(1) + Mat(2,2) * Vec(2);
    return Result;
}

int wmain(int argc, wchar_t *argv[]/*, wchar_t *envp[]*/)
{
    Matrix<float> Mat1({2,  0,  0,
                        0,  2,  0,
                        0,  0,  2});

    Vector<float> Vec1({1,
                        2,
                        3});

    Matrix<float> Mat2 = Mat1 * 2;      // Matrix-Scalar Multiplication
    Vector<float> Vec2 = Mat1 * Vec1;   // Matrix-Vector Multiplication

    return 0;
}

Masalahnya, ketika saya mencoba melakukan perkalian matriks-vektor, compiler memilih dan mencoba menggunakan operator perkalian matriks-skalar, dan memberikan error compiler.

Jika saya menghapus operator perkalian matriks-skalar dan baris tempat saya menggunakannya, maka program berhasil berjalan. Sebaliknya, jika saya menghapus operator perkalian matriks-vektor, operator tersebut berhasil dijalankan kembali. Mereka hanya tidak akur. Dan ketika dijalankan (dalam kedua kasus), ia membuat semua perhitungan dengan benar.

Apa yang salah di sini?

Kompiler & IDE: Edisi Komunitas Microsoft Visual Studio 2015


person hkBattousai    schedule 08.03.2016    source sumber
comment
Bisakah Anda memberikan contoh minimal? Ini memiliki banyak rincian yang tidak diperlukan.   -  person TartanLlama    schedule 08.03.2016
comment
@TartanLlama Asli saya terdiri dari kelas matriks dan vektor BESAR. Di sini saya hanya membawa fungsi-fungsi penting yang akan saya gunakan dalam contoh minimal ini. Ini sudah merupakan versi yang sangat disederhanakan. Saya tidak tahu di mana lagi saya bisa memotong.   -  person hkBattousai    schedule 08.03.2016
comment
@SimonKraemer Ah, maaf, saya sudah memperbaikinya.   -  person hkBattousai    schedule 08.03.2016
comment
template<class S> ... (const S&) cocok dengan semua objek. Itu tidak berarti hanya tipe skalar atau semacamnya   -  person M.M    schedule 08.03.2016
comment
Penyelesaian kelebihan beban menjadi sangat rumit bila Anda memiliki versi anggota dan non-anggota dari operator yang sama. Saya sangat menyarankan hanya menggunakan operator* non-anggota. (Ini mungkin tidak akan menyelesaikan masalah Anda tetapi mungkin menghilangkan beberapa faktor yang mengganggu atau perancu)   -  person M.M    schedule 08.03.2016
comment
Jadikan Matrix<T> operator*(const S & Scalar) const.   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2016
comment
@M.M atau ubah urutan definisi tipe dan buat kedua fungsi anggota. Atau cukup gunakan T sebagai ganti parameter templat yang berbeda.   -  person Simon Kraemer    schedule 08.03.2016
comment
@hkBattousai Adakah alasan khusus Anda tidak menerima T untuk operator anggota Anda?   -  person Simon Kraemer    schedule 08.03.2016
comment
@M.M pengurutan parsial menjadikan const Vector<T>& lebih disukai daripada const S&, tidak ada yang menjadi rumit dalam operator anggota vs. nonanggota, setidaknya di sini anggota operator* mungkin harus memenuhi syarat const, meskipun dentang masih menganggapnya ambigu   -  person Piotr Skotnicki    schedule 08.03.2016
comment
@SimonKraemer Tidak ada alasan khusus. Saya hanya ingin membuatnya seumum mungkin.   -  person hkBattousai    schedule 08.03.2016
comment
@hkBattousai Ubah menjadi hanya menerima T yang akan menyelesaikan masalah Anda. Hal ini juga harus mengurangi rawan kesalahan. Melewati mis. sebuah pointer tidak masuk akal tetapi berfungsi dengan pendekatan Anda saat ini. Tetapi perhatikan juga hal-hal seperti kebenaran const dan hal-hal lain yang disebutkan dalam komentar di atas.   -  person Simon Kraemer    schedule 08.03.2016
comment
@hkBattousai Ini yang saya sebut sebagai contoh minimal.   -  person TartanLlama    schedule 08.03.2016
comment
@PiotrSkotnicki tampak seperti bug dentang bagi saya.   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2016
comment
@TartanLlama Anda melewatkan return *this; di operator anggota ;-)   -  person Simon Kraemer    schedule 08.03.2016
comment
@n.m. baiklah aku tidak sepenuhnya yakin. Dalam versi global, T digunakan dua kali, untuk Matrix dan Vector, jadi dentang mungkin berpikir bahwa tidak ada yang lebih terspesialisasi daripada yang lain, sehingga menimbulkan ambiguitas. Matrix<float> lebih terspesialisasi dari Matrix<T>, sama seperti const Vector<T>& lebih terspesialisasi dari const S&   -  person Piotr Skotnicki    schedule 08.03.2016
comment
@hkBattousai solusi termudah dan paling portabel adalah menjadikan anggota operator* menjadi anggota global, dan membiarkan T dari Matrix<T> disimpulkan (seperti pada versi Vector<T>)   -  person Piotr Skotnicki    schedule 08.03.2016
comment
@Piotr tidak jelas apakah float harus masuk ke dalam gambar. Apakah kita memesan sebagian Matrix<float>::operator* atau Matrix<T>::operator*?   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2016
comment
@n.m. Saya kira ketika seseorang menggunakan Matrix<float>, maka kompiler berakhir dengan dua kelebihan: template<class S> operator*(const Matrix<float>&, const S&) dan template<class T> operator*(const Matrix<T>&, const Vector<T>&), yaitu kompiler melihat float, karena Matrix<float> adalah parameter objek implisit   -  person Piotr Skotnicki    schedule 08.03.2016
comment
@Piotr Rupanya gcc dan msvc berpikir sebaliknya.   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2016
comment
@n.m. ya, saya tahu, dan saya tidak tahu pasti siapa yang benar, meski saya mendukung dentang   -  person Piotr Skotnicki    schedule 08.03.2016
comment
@Piotr Sekarang saya menemukan kasus tiga kompiler yang membuat tiga keputusan berbeda. pastebin.com/Bp8Us9EA   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2016
comment
@PiotrSkotnicki dentang 3.3 dulu menerima program ini, 3.5 tidak. Perbaikan bug atau bug baru?   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2016
comment
@n.m. dentang menerima kode yang Anda tempelkan tautannya (memilih operator anggota, yang saya setujui), apakah Anda yakin gagal dengan 3.5?   -  person Piotr Skotnicki    schedule 08.03.2016
comment
Ya, dentang menerima, gcc menolak, dan msvc menerima dan memilih operator mandiri.   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2016


Jawaban (1)


Bahasa ini tidak menentukan hal itu saat mencari kelebihan beban untuk memeriksa semua cakupan yang mungkin. Segera setelah satu kandidat ditemukan, hanya kelebihan dalam cakupan tersebut yang digunakan untuk memilih kandidat terbaik. Jika kandidat tersebut gagal mengkompilasi karena alasan lain, kandidat tersebut tetap tidak akan memeriksa cakupan tambahan.

Jadi dalam kasus Anda karena skalar adalah parameter templat, ia akan cocok dengan jenis apa pun selama operan sebelah kiri dari operator adalah Matrix<T>, perkalian skalar akan cocok dengan operan sebelah kanan mana pun.

Anda bisa:

  • Buat saja keduanya operator* pada cakupan yang sama sehingga keduanya dipertimbangkan untuk resolusi kelebihan beban.
  • Ubah skalar operator menjadi misalnya Scalar<T> yang merupakan pembungkus tipis di sekitar tipe skalar daripada tipe templat umum.
person Mark B    schedule 15.03.2016