2012-12-17 49 views
8

Tôi gặp một điểm thú vị mà tôi không thể giải thích hoặc tìm giải thích cho. Hãy xem xét các định nghĩa mẫu sau (biên soạn với mingw g ++ 4.6.2): ​​Chuyên môn không chuyên môn về mẫu lớp

template <typename T, typename S> 
class Foo 
{ 
public: 
    void f(){} 
    void g(){} 
}; 

Chúng ta có nên muốn, chúng ta có thể hoàn toàn bất kỳ chức năng chuyên viên duy nhất:

template <> 
void Foo<char,int>::f() {} 

Nhưng phần chuyên môn hóa không thành công với một " sử dụng không hợp lệ của loại không đầy đủ 'lớp Foo < ...>'" lỗi:

template <typename T, typename S> 
void Foo<T,S*>::f() 
{ 
} 

template <typename T> 
void Foo<T,int>::f() 
{ 
} 

Và tôi không thể tìm ra lý do tại sao. Đó là một quyết định thiết kế có ý thức được thực hiện để tránh một số vấn đề tôi không thể thấy trước? Nó là một sự giám sát?

Xin cảm ơn trước.

+0

Hầu như trùng lặp: http://stackoverflow.com/questions/12335762/partial-specialization-of-member-function và http://stackoverflow.com/questions/165101/invalid-use-of-incomplete-type- chuyên biệt về lỗi-từng phần-mẫu. – jogojapan

Trả lời

6

Khái niệm chuyên môn hóa một phần chỉ tồn tại cho các lớp mẫu (mô tả bởi §14.5.5) và các mẫu thành viên (tức là thành viên của một lớp mẫu mà bản thân chức năng mẫu, được mô tả bởi §14.5.5.3/2). Nó không tồn tại đối với các thành viên thông thường của các mẫu lớp, cũng như không tồn tại đối với các mẫu hàm – đơn giản vì nó không được mô tả trong tiêu chuẩn.

Bây giờ, bạn có thể tranh luận rằng bằng cách đưa ra định nghĩa của một đặc tả từng phần của một hàm thành viên, chẳng hạn như

template <typename T> 
void Foo<T,int>::f() 
{ } 

bạn ngầm định nghĩa một đặc tả từng phần của mẫu lớp: Foo<T,int>.Đó là, tuy nhiên, dứt khoát loại trừ khả năng bởi các tiêu chuẩn:

(§14.5.5/2) Each class template partial specialization is a distinct template and definitions shall be provided for the members of a template partial specialization (14.5.5.3).

(§14.5.5.3/1) [...] The members of the class template partial specialization are unrelated to the members of the primary template. Class template partial specialization members that are used in a way that requires a definition shall be defined; the definitions of members of the primary template are never used as definitions for members of a class template partial specialization. [...]

Sau đó ngụ ý rằng nó là không thể để ngầm định nghĩa một đặc tả từng phần bằng cách đơn giản đưa ra các định nghĩa của một trong những thành viên của nó: Sự tồn tại rất của thành viên đó sẽ không theo định nghĩa của mẫu chính, do đó xác định nó tương đương với xác định chức năng thành viên không phải là được khai báo và không được phép (ngay cả với các lớp không phải mẫu).

Mặt khác, khái niệm về chuyên môn hóa rõ ràng (hoặc đầy đủ chuyên môn, như bạn gọi nó) tồn tại cho hàm thành viên của lớp mẫu. Nó được mô tả một cách rõ ràng bởi các tiêu chuẩn:

(§14.7.3/1) An explicit specialization of any of the following:
[...]
— member function of a class template
[...]
can be declared by a declaration introduced by template<>; [...]

§14.7.3/14 mô tả chi tiết:

(§14.7.3/14) A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition. [...]

Do đó, đối với chuyên ngành rõ ràng của các thành viên, các instantiation của phần còn lại của lớp mẫu công trình ngụ ý – nó được lấy từ định nghĩa mẫu chính, hoặc bất kỳ chuyên môn từng phần nào nếu được xác định.

+0

Có những gì tôi đang tìm kiếm. Không thể giải mã các phần liên quan của tiêu chuẩn. Cảm ơn :) – StoryTeller

3

Tôi nghĩ rằng sự khác biệt là khi bạn thực hiện (có giá trị) chuyên môn hóa rõ ràng đầu tiên của f:

template <> 
void Foo<char,int>::f() {} 

Bạn đang làm một instantiation ngầm của Foo<char,int>. Nhưng khi bạn thử các đặc tả từng phần với:

template <typename T> 
void Foo<T,int>::f() 
{ 
} 

Trình biên dịch sẽ cần phải nhanh chóng ngầm Foo<T,int> trước khi thực hiện chuyên môn hóa, nhưng nó không thể làm điều đó vì sự T. Và nó không thành công.

Bạn có thể kiểm tra xem là trường hợp với đoạn mã sau:

template <typename T, typename S> 
class Foo 
{ 
public: 
    void f(){} 
    void g(){} 
}; 


template <> 
void Foo<char,int>::f() //line 11 
{} 

template <> 
class Foo<char,int> //line 15 
{}; 

Với g++ nó mang lại cho các lỗi:

test.cpp:15:7: error: specialization of ‘Foo<char, int>’ after instantiation 
test.cpp:15:7: error: redefinition of ‘class Foo<char, int>’ 
test.cpp:2:7: error: previous definition of ‘class Foo<char, int>’ 

Với clang++ là một chút rõ ràng hơn:

test.cpp:15:7: error: explicit specialization of 'Foo<char, int>' after instantiation 
class Foo<char,int> 
     ^~~~~~~~~~~~~ 
test.cpp:11:6: note: implicit instantiation first required here 
void Foo<char,int>::f() 
    ^
+0

Cảm ơn, đó là sâu sắc :) – StoryTeller

5

Tôi đã cố gắng tìm một trích dẫn ngắn gọn từ tiêu chuẩn, nhưng tôi không nghĩ ere là một. Thực tế là, không có điều gì như là một chuyên môn hóa một phần của một hàm mẫu (hoặc, cho rằng vấn đề, của một bí danh mẫu). Chỉ các mẫu lớp mới có thể có các chuyên môn từng phần.

Hãy quên các mẫu trong một giây. Trong C++, có một sự khác biệt lớn giữa tên lớp và tên hàm. Chỉ có một định nghĩa của một lớp trong một phạm vi nhất định. (Bạn có thể có các khai báo khác nhau, nhưng tất cả chúng đều đề cập đến một lớp thực.) Vì vậy, tên thực sự xác định lớp.

Tên chức năng, mặt khác, là một loại nhận dạng nhóm. Bạn có thể xác định bất kỳ số lượng hàm nào trong phạm vi có cùng tên chính xác. Khi bạn sử dụng tên hàm để gọi hàm, trình biên dịch phải tìm ra hàm nào bạn thực sự có nghĩa là bằng cách xem xét các khả năng khác nhau và khớp chữ ký của mỗi hàm với các đối số được cung cấp. Không có mối quan hệ giữa các chức năng khác nhau có cùng tên; họ là những thực thể hoàn toàn riêng biệt.

Vì vậy, không có vấn đề gì lớn. Bạn biết tất cả điều này, phải không? Nhưng bây giờ chúng ta hãy quay trở lại các mẫu.

Tên của một lớp templated vẫn là duy nhất.Mặc dù bạn có thể xác định các chuyên môn từng phần, bạn phải rõ ràng chuyên lớp tương tự. Cơ chế này trông bề ngoài giống như thuật toán phân giải tên hàm được đề cập ở trên, nhưng có sự khác biệt đáng kể - một trong số đó là, không giống như các nguyên mẫu hàm, bạn không thể có hai mẫu lớp trong cùng phạm vi với các kiểu tham số mẫu khác nhau.

Chức năng được mạ điện, mặt khác, không cần phải xác định tên duy nhất. Templating không thay thế cơ chế quá tải chức năng bình thường. Vì vậy, khi trình biên dịch đang cố gắng tìm ra tên hàm có nghĩa là gì, nó phải xem xét tất cả các khai báo được tạo khuôn mẫu và không có khuôn mẫu cho tên hàm đó, giải quyết các lệnh được định sẵn cho một tập các tham số mẫu (nếu có thể) và sau đó một khi nó có một danh sách các đối tượng chức năng có thể, chọn đối tượng tốt nhất với độ phân giải quá tải bình thường.

Đó là một thuật toán khá khác với độ phân giải tham số mẫu lớp mẫu. Thay vì chỉ khớp một danh sách các đối số mẫu được cung cấp với một danh sách các tham số mẫu đã khai báo, đó là cách nó giải quyết các mẫu lớp, nó phải lấy từng hàm khuôn mẫu có thể khớp nhau (ví dụ: ít nhất số tham số phù hợp) ; suy ra các tham số mẫu bằng cách hợp nhất các đối số được cung cấp với mẫu; và sau đó thêm chuyên môn giải quyết vào bộ quá tải cho một vòng tiếp theo của độ phân giải quá tải.

Tôi cho rằng có thể đã thêm độ phân giải một phần vào quá trình đó, nhưng các tương tác giữa chuyên môn từng phần và quá tải hàm tấn công tôi có khả năng dẫn đến hành vi giả huyền diệu. Trong trường hợp, nó không cần thiết và do đó không có cơ chế như vậy. (Bạn hoàn toàn có thể chuyên về một mẫu chức năng. Chuyên môn đầy đủ có nghĩa là không có đối số mẫu để suy luận, do đó, nó không phải là vấn đề.)

Vì vậy, đó là scoop: bạn không thể chuyên chức năng một phần, nhưng có không có gì ngăn cản bạn cung cấp bất kỳ số lượng mẫu chức năng nào có cùng tên. Tất cả chúng sẽ được xem xét ở độ phân giải quá tải, và cái tốt nhất sẽ thắng, như thường lệ.

Thông thường, điều đó thực sự đủ cho nhu cầu quá tải của bạn. Bạn nên suy nghĩ về các hàm templated giống như cách bạn nghĩ về các hàm bình thường: đưa ra một cách để chọn một hàm bạn muốn dựa trên các đối số được cung cấp. Nếu bạn cảm thấy bạn thực sự cần cung cấp các tham số mẫu trong một cuộc gọi hàm, thay vì để chúng được suy luận, chỉ cần làm cho hàm thành viên (có thể là tĩnh) của một lớp templated và cung cấp các đối số mẫu cho lớp đó.

Hy vọng rằng sẽ giúp ...

+0

Nó giúp đỡ, rất nhiều trong thực tế. Cảm ơn :) – StoryTeller

Các vấn đề liên quan