C ++ - Tại sao từ khóa 'mẫu' được yêu cầu ở đây?


9

Tôi có đoạn mã sau:

template <typename TC>
class C
{
    struct S
    {
        template <typename TS>
        void fun() const
        {}
    };

    void f(const S& s)
    {
        s.fun<int>();
    }
};

// Dummy main function
int main()
{
    return 0;
}

Khi xây dựng điều này với cả gcc 9.2 và clang (9.0), tôi gặp lỗi biên dịch do templatetừ khóa được yêu cầu để gọi fun. Clang cho thấy:

error: use 'template' keyword to treat 'fun' as a dependent template name
        s.fun<int>();
          ^
          template 

Tôi không hiểu tại sao trình biên dịch nghĩ funlà một tên phụ thuộc trong ngữ cảnh f, vì fbản thân nó không phải là một mẫu. Nếu tôi thay đổi Cthành một lớp thông thường thay vì một mẫu, lỗi sẽ biến mất; tuy nhiên, tôi không hiểu tại sao cần có một lỗi ở nơi đầu tiên kể từ khi không phải Svà cũng không fphụ thuộc vào TC.

Thật kỳ lạ, MSVC 19,22 biên dịch điều này chỉ tốt.


Ghi chú

Trước khi bỏ phiếu để đóng dưới dạng dupe của Where và tại sao tôi phải đặt từ khóa "template" và "typename"? vui lòng xem đây là trường hợp đặc biệt, ngay cả khi Sthực sự là một tên phụ thuộc, trong bối cảnh của fnó sẽ không bị phụ thuộc nếu không phải vì thực tế rằng chúng là thành viên của việc khởi tạo hiện tại.


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Bhargav Rao

Câu trả lời:


10

Hãy xem xét :

template<typename T>
struct C
{
    struct S
    {
        int a = 99;
    };

    void f(S s, int i)
    {
        s.a<0>(i);
    }
};

template<>
struct C<long>::S
{
    template<int>
    void a(int)
    {}
};

int main()
{
    C<int>{}.f({}, 0); // #1
    C<long>{}.f({}, 0); // #2
}

s.a<0>(i)được phân tích cú pháp dưới dạng một biểu thức có chứa hai thao tác so sánh <>, điều này tốt cho # 1 nhưng không thành công cho # 2.

Nếu điều này được thay đổi thành s.template a<0>(i)# 2 thì OK và # 1 thất bại. Do đó, templatetừ khóa không bao giờ là dư thừa ở đây.

MSVC có khả năng diễn giải biểu thức theo s.a<0>(i)cả hai cách trong cùng một chương trình. Nhưng điều này không đúng theo Tiêu chuẩn; mỗi biểu thức chỉ nên có một phân tích cú pháp để trình biên dịch xử lý.


Tôi vẫn chưa hoàn toàn hiểu điều này. Ví dụ của bạn cho thấy rằng trong trường hợp này, bạn có thể sử dụng một chuyên ngành Choặc khác, nhưng bạn không thể khởi tạo cả hai. Các templatetừ khóa ở đây vẫn không cần thiết IMHO, vì đó Sđược chọn phụ thuộc vào Cbạn nhanh chóng. Nếu không có templatetừ khóa, bạn có thể khởi tạo cả hai và hành vi của f sẽ khác nhau đối với từng trường hợp.
Martin

2
@Martin quan điểm là mỗi mã thông báo chỉ có một vai trò cú pháp trong tệp nguồn. Ví dụ: mã thông báo không <phải là toán tử so sánh trong một lần khởi tạo mẫu và khung mở góc trong một lần khởi tạo khác. Điều này là để đảm bảo rằng trình biên dịch có thể phân tích các mẫu cho AST (với trình giữ chỗ cho các loại mẫu).
ecatmur

Điều đó có ý nghĩa. Cảm ơn!
Martin

7

funcó thể hoặc không thể là một hàm mẫu (hoặc hoàn toàn không tồn tại) tùy thuộc vào tham số mẫu của class C.

Đó là bởi vì bạn có thể chuyên S(không cần chuyên C):

template <> struct C<int>::S {};

Bởi vì trình biên dịch muốn biết có phải funlà một mẫu hay không khi lần đầu tiên nhìn vào class C(trước khi thay thế tham số mẫu), templatelà bắt buộc.


1
tâm trí ... thổi ...
bolov

Sau đó, việc tiếp theo là liệu những định nghĩa mới này Scó thể được truy cập bằng cách nào không f. Nếu họ không thể, việc hạn chế này sẽ không có ý nghĩa vì fdù sao cũng không thể nhìn thấy họ.
Martin

@Martin Với cả GCC, Clang và MSVC fđều tìm thấy nó.
HolyBlackCat

@bolov Vâng, bạn có thể chuyên nhiều thứ khác nhau .
HolyBlackCat
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.