Template Kelebihan Ambigu

16

Saya memiliki kode templated berikut

#include <vector>
#include <array>
#include <iostream>

template<typename T1>
void foo(std::vector<T1> bar) {
    std::cout << "GENERIC" << std::endl;
}

template<typename T1>
void foo(std::vector<std::vector<T1>> bar) {
    std::cout << "SPECIFIC (vector)" << std::endl;
}

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::vector<int>> a(2, std::vector<int> { 1, 2, 3});
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(a);
    foo(b);
}

yang menghasilkan

SPECIFIC (vector)
GENERIC

Saya bertanya-tanya mengapa versi vektor-of-vektor dipanggil dengan templat khusus, tetapi versi vektor-of-array disebut dengan generik?

Xaser
sumber
2
FYI: Anda dapat menyederhanakan ini, dengan masalah yang sama, dengan menghapus bagian luar vectorpada semuanya. Lihat di sini
ChrisMM
@ ChrisMM tangkapan yang bagus. Contoh ini disintesis dari kode produksi saya, di mana struktur bersarang diperlukan.
Xaser
5
MSVC memanggil versi vektor-array: godbolt.org/z/7Gfeb0
R2RT

Jawaban:

8
template<typename T1, size_t SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

Anda harus menggunakan std::size_tbukan int. lari kesini

Sunting: Sebenarnya, komentar dan intuisi saya tentang kode membuat saya menggali topik. Pada pandangan pertama, pengembang standar (seperti saya) mengharapkan kompiler untuk dikonversi intke std::size_t(karena mereka adalah tipe integral dan secara implisit mengkonversi sangat sepele) dan memilih void foo(std::vector<std::array<T1, SIZE>> bar)spesialisasi terbaik. Jadi ketika membaca halaman pengurangan argumen template saya menemukan ini:

Jika parameter templat non-tipe digunakan dalam daftar parameter, dan argumen templat yang sesuai dideduksi, jenis argumen templat yang dideduksi (sebagaimana ditentukan dalam daftar parameter templat terlampir, makna referensi dipertahankan) harus cocok dengan jenis parameter template non-tipe persis, kecuali bahwa cv-kualifikasi dijatuhkan, dan kecuali di mana argumen template disimpulkan dari suatu array array-dalam hal itu tipe integral apa pun diperbolehkan, meskipun bool meskipun itu selalu menjadi benar:

Seperti biasa, tentu saja, Anda harus membaca beberapa kali lebih banyak dari sekali untuk memahami artinya :)

Jadi hasil yang menarik keluar.

Sudah spesialisasi yang kami inginkan tidak dipilih tetapi jika kompiler terpaksa memilih, itu akan menjadi kesalahan.

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(b); // P = std::vector<std::array<int,(int)SIZE>
            // A = std::vector<std::array<int,(unsigned_long)SIZE>>
            // error: deduced non-type template argument does not have the same
            // type as its corresponding template argument */
}

jalankan kode

Hal menarik lainnya adalah:

Jika argumen templat non-tipe tidak dideduksi, tidak akan ada batasan yang memaksa argumen dan tipe templat sama.

#include <vector>
#include <array>
#include <iostream>

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo<int,3>(b);
}

jalankan kode

arnes
sumber
@Xaser karena argumen templat kedua array bertipe size_t...
Jean-Baptiste Yunès
2
Mempertimbangkan komentar R2RT, tampaknya ada perbedaan spesifik penyusun.
Xaser
8

Saya pikir ini hanya karena satu baris dari[temp.deduct.call]/4

Secara umum, proses deduksi mencoba untuk menemukan nilai argumen templat yang akan membuat deduksi A identik dengan A

Untuk memperjelas, Aberarti parameter, dari[temp.deduct.call]/1

... pengurangan argumen templat dengan jenis argumen panggilan yang sesuai (sebut saja A) ...

Seperti yang telah ditunjukkan, mengubah template<typename T1, int SIZE>untuk template<typename T1, size_t SIZE>memperbaiki masalah yang Anda lihat. Seperti yang dinyatakan dalam [temp.deduct.call]/4, kompiler sedang mencari untuk menyimpulkan Ayang identik dengan A. Karena std::arrayargumen templat memiliki <class T, size_t N>(dari [array.syn]), parameter kedua sebenarnya size_t, bukan int.

Oleh karena itu, untuk pengurangan templat, fungsi generik Anda template<typename T1>mampu mencocokkan dengan tepat jenis A, di mana-karena spesialis Anda template<typename T1, int SIZE>bukan pencocokan tepat . Saya percaya MSVC salah dalam deduksi.

ChrisMM
sumber