Mẫu quá tải mơ hồ


16

Tôi có mã templated sau đây

#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);
}

sản xuất

SPECIFIC (vector)
GENERIC

Tôi đang tự hỏi tại sao phiên bản vectơ của vectơ được gọi với mẫu cụ thể, nhưng phiên bản vectơ của mảng được gọi với chung?


2
FYI: Bạn có thể đơn giản hóa điều này, với cùng một vấn đề, bằng cách loại bỏ phần bên ngoài vectortrên tất cả chúng. Xem tại đây
ChrisMM

@ChrisMM bắt tốt. Ví dụ này được tổng hợp từ mã sản xuất của tôi, trong đó cấu trúc lồng nhau là cần thiết.
Xaser

5
MSVC gọi phiên bản vectơ của mảng: godbolt.org/z/7Gfeb0
R2RT

Câu trả lời:


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

Bạn nên sử dụng std::size_tthay vì int. chạy ở đây

Chỉnh sửa: Trên thực tế, ý kiến ​​của bạn và trực giác của tôi về mã đã khiến tôi đi sâu vào chủ đề. Thoạt nhìn, một nhà phát triển tiêu chuẩn (như tôi) mong muốn trình biên dịch chuyển đổi intthành std::size_t(vì cả hai đều là loại tích phân và chuyển đổi ngầm là rất tầm thường) và chọn void foo(std::vector<std::array<T1, SIZE>> bar)là chuyên môn tốt nhất. Vì vậy, trong khi đọc trang khấu trừ đối số mẫu tôi đã tìm thấy điều này:

Nếu tham số mẫu không loại được sử dụng trong danh sách tham số và đối số mẫu tương ứng được suy ra, loại đối số mẫu được suy ra (như được chỉ định trong danh sách tham số mẫu kèm theo, có nghĩa là tham chiếu được bảo tồn) phải khớp với loại của Tham số mẫu không phải là chính xác, ngoại trừ các vòng loại cv bị loại bỏ và ngoại trừ trường hợp đối số mẫu được suy ra từ một mảng ràng buộc trong trường hợp đó, bất kỳ loại tích phân nào cũng được cho phép, ngay cả bool mặc dù nó luôn luôn đúng:

Như mọi khi, tất nhiên, bạn phải đọc nhiều lần hơn một lần để hiểu ý nghĩa của nó :)

Vì vậy, một kết quả thú vị đi ra.

Đã chuyên môn mong muốn của chúng tôi không được chọn nhưng nếu trình biên dịch đã buộc phải chọn, đó sẽ là một lỗi.

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 */
}

mã vận hành

Một điều thú vị khác là:

Nếu đối số mẫu không loại không được suy luận, sẽ không có hạn chế nào buộc các loại đối số và loại mẫu giống nhau.

#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);
}

mã vận hành


@Xaser vì đối số mẫu thứ hai của mảng là loại size_t...
Jean-Baptiste Yunès

2
Xem xét nhận xét của R2RT, dường như có sự khác biệt cụ thể của trình biên dịch.
Xaser

8

Tôi nghĩ rằng điều này chỉ đơn giản là do một dòng từ[temp.deduct.call]/4

Nói chung, quá trình khấu trừ cố gắng tìm các giá trị đối số mẫu sẽ làm cho suy luận A giống với A

Để làm rõ, Acó nghĩa là tham số, từ[temp.deduct.call]/1

... khấu trừ đối số mẫu với loại đối số tương ứng của cuộc gọi (gọi nó là A) ...

Như đã được chỉ ra, thay đổi template<typename T1, int SIZE>để template<typename T1, size_t SIZE>khắc phục vấn đề bạn đang thấy. Như đã nêu trong [temp.deduct.call]/4, trình biên dịch đang tìm cách suy ra một Acái giống hệt với A. Vì một std::arrayđối số có mẫu <class T, size_t N>(từ [array.syn]), nên trên thực tế size_t, tham số thứ hai là không int.

Do đó, đối với việc khấu trừ mẫu, chức năng chung của bạn template<typename T1>có thể khớp chính xác với loại A, trong đó - vì chuyên ngành của bạn template<typename T1, int SIZE>không phải là một kết hợp chính xác . Tôi tin rằng MSVC không chính xác trong khấu trừ của nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.