Bạn có thực sự thấy rằng việc sử dụng cho định nghĩa mẫu không, ví dụ, không phải là khai báo mẫu?
Một số sử dụng:
// declaration only: the parameter name has no use beyond documentation
template<typename>
struct A;
// this is fine
template<typename T>
void eat_an_a(A<T> a);
// later, we can name the parameter to use it
template<typename T>
struct A { ... };
// C++0x only
template<
typename T
// We don't care for the actual type (which will default to void)
// the goal is sfinae
, typename = typename std::enable_if<
std::is_array<typename std::decay<T>::type>::value
>::value
>
void
f(T&& t);
// We still don't care to name that defaulted parameter
template<typename T, typename>
void f(T&& t)
{ ... }
Lời giải thích cho trường hợp rất đặc biệt mà bạn có liên quan đến đã được đưa ra bởi Johannes, nhưng dường như bạn đã tìm thấy nó không đạt yêu cầu. Tôi sẽ hướng dẫn bạn cách làm việc này. Hãy cho chúng tôi giả định một đặc điểm tùy ý:
// no definition
template<typename TypeToExamine, typename ImplementationDetail = void>
struct trait;
Tôi viết đúng vai trò của các thông số loại trong tên của chúng. Bây giờ những gì khai báo này cho phép, vì tham số thứ hai được mặc định, là một chút đường cú pháp. Bất cứ nơi nào trait<U>
xuất hiện, chính là chính xác như thể chúng tôi đã viết trait<U, void>
đã được viết. Bây giờ chúng ta hãy cung cấp định nghĩa cho trường hợp cơ sở của đặc điểm của chúng tôi:
// assume previous declaration is still in scope so we do not default
// the second parameter again
template<typename T, typename> struct trait: std::false_type {};
Nó không phải là một đặc điểm rất hữu ích. Bây giờ, khi chúng tôi viết trait<U>
, viết tắt là trait<U, void>
, chúng tôi sẽ kết thúc với định nghĩa này. Điều này có nghĩa là trait<U>::value
hợp lệ và thực tế là false
. Hãy làm cho lớp của chúng tôi hữu ích hơn bằng cách thêm các thành phần bí mật:
template<typename> struct void_ { typedef void type; };
// again, assume previous declarations are in scope
template<typename T, typename void_<decltype(T() + T())>::type>
struct trait: std::true_type {};
Một lần nữa, khi chúng ta viết trait<U>
, nó như thể chúng tôi đã viết trait<U, void>
. Chuyên môn từng phần không thay đổi điều đó (nó không được phép). Nhưng chúng ta nên sử dụng định nghĩa nào khi truy vấn trait<U>::value
? Đầu tiên, chúng ta phải biết chính xác chuyên môn nào phù hợp; hoặc, đối số thứ hai bí ẩn typename void_<decltype(T() + T())>::type
là gì?
Trường hợp đơn giản nhất là khi U() + U()
không đúng định dạng. Sau đó, SFINAE đá vào và dường như chuyên môn không tồn tại; do đó, chúng tôi có được định nghĩa không chuyên biệt và value
là false
. Tuy nhiên, nếu U() + U()
được tạo đúng, thì decltype
sẽ tạo ra một loại và toàn bộ biến thành void
, vì tất cả các loại void_<T>::type
là void
. Vì vậy, điều này có nghĩa rằng chúng tôi có một chuyên môn của mẫu trait<T, void>
. Điều này có thể khớp với trait<U>
, chỉ cần đối sánh T
với U
. Và bây giờ value
là true
.
Tuy nhiên, nếu chuyên môn đã được viết
template<typename T>
struct trait<T, decltype(T() + T())>: std::true_type {};
thì cách duy nhất nó sẽ được sử dụng là khi viết trait<U, decltype(U() + U())>
, trừdecltype(U() + U())
happended để được bãi bỏ. Hãy nhớ rằng, trait<U>
là đường cho trait<U, void>
. Vì vậy, trait<int>
sẽ không bao giờ khớp với chuyên môn của chúng tôi bởi vì sau này có dạng trait<int, int>
.
Vì vậy, void_
đóng vai trò luôn có chuyên môn của biểu mẫu trait<T, void>
nếu chúng không phải là SFINA. Vì chúng ta không quan tâm đến việc sử dụng tham số kiểu, nó không được đặt tên.
Bạn có thể giải thích chi tiết về điểm 1 không? – iammilind
@iammiling: Làm thế nào? –