2016-05-09 12 views
13

Mã mẫu 1:sự khác biệt giữa tên thông số mẫu so với tên tệp không?

namespace detail { 
    enum enabler { dummy }; 
} 

class foo { 
public: 
    template <typename T, 
       typename std::enable_if<!std::is_integral<T>::value, 
             detail::enabler>::type = detail::enabler::dummy> 
    void func(T t) { 
     std::cout << "other" << std::endl; 
    } 

    template <typename T, 
       typename std::enable_if<std::is_integral<T>::value, 
             detail::enabler>::type = detail::enabler::dummy> 
    void func(T t) { 
     std::cout << "integral" << std::endl; 
    } 
}; 

Mã mẫu 2:

namespace detail { 
    enum enabler { dummy }; 
} 

class foo { 
public: 
    template <typename T, 
       typename T2 = typename std::enable_if<!std::is_integral<T>::value, detail::enabler>::type> 
    void func(T t) { 
     std::cout << "other" << std::endl; 
    } 

    template <typename T, 
       typename T2 = typename std::enable_if<std::is_integral<T>::value, detail::enabler>::type> 
    void func(T t) { 
     std::cout << "integral" << std::endl; 
    } 
}; 

Tôi hiểu tại sao mẫu 2 không biên dịch. Về cơ bản vì cả hai hàm mẫu giống nhau (chúng thậm chí có cùng các tham số mẫu T, T2).

Tôi không hiểu tại sao mẫu 1 biên dịch lại! Tôi thấy rằng đó là cùng một vấn đề. Về cơ bản thay vì có tham số mẫu thứ hai là typename, đây là một số enum thay vì typename lần này.

Ai đó có thể giải thích được không?

Hơn nữa, tôi đã thực thi đoạn mã sau đây và nó trả về sự thật thậm chí còn khó hiểu hơn! (Làm cho cảm giác rằng đó là sự thật nhưng khó hiểu rằng mẫu 1 biên dịch)

std::cout 
    << std::boolalpha 
    << std::is_same<std::enable_if<std::is_integral<int>::value, int>::type, 
        std::enable_if<!std::is_integral<float>::value, int>::type>::value 
    << std::endl; 
+0

Điều này không thực sự liên quan đến yêu cầu đối với 'typename' rất nhiều vì đây là câu hỏi về cách SFINAE hoạt động. – Niall

+0

Điều gì đáng ngạc nhiên về bit cuối cùng? Chúng tôi có 'enable_if :: type', hai lần. – Barry

Trả lời

7

Đối số mặc định (cho dù đối số hàm mặc định hoặc đối số mẫu mặc định) không phải là một phần của chữ ký hàm. Bạn chỉ được phép xác định một hàm với một chữ ký đã cho.

Trong mẫu mã 1, nếu chúng ta thả tên của các đối số và tất cả các giá trị mặc định, chúng ta có hai chức năng đó là:

template <typename T, std::enable_if_t<!std::is_integral<T>::value, detail::enabler>> 
void func(T) { ... } 

template <typename T, std::enable_if_t<std::is_integral<T>::value, detail::enabler>> 
void func(T) { ... } 

Đó là hai chữ ký khác nhau - các tham số mẫu thứ 2 có các loại khác nhau trong cả hai hàm - vì vậy nó hợp lệ từ phối cảnh này.

Bây giờ, điều gì sẽ xảy ra khi chúng tôi thực sự gọi nó. Nếu T là một loại không thể thiếu, đối số thứ 2 được thay thế trong cả hai trường hợp:

template <typename T, ????>     void func(T) { ... } 
template <typename T, detail::enabler = dummy> void func(T) { ... } 

Trong chức năng đầu tiên, khái niệm typename std::enable_if<false, detail::enabler>::type là vô hình thành. Không có type typedef trên loại đó. Thông thường, viết mã hình thành không đúng là một lỗi trình biên dịch cứng. Tuy nhiên, nhờ quy tắc được gọi là SFINAE (Lỗi thay thế không phải là lỗi), mã hình thành không phát sinh trong bối cảnh ngay lập tức thay thế mẫu không phải là lỗi - nó chỉ khiến cho chuyên môn về chức năng/lớp mẫu bị xóa khỏi bộ cân nhắc .Vì vậy, chúng tôi kết thúc chỉ với một hợp lệ:

template <typename T, detail::enabler = dummy> void func(T) { ... } 

được gọi.


Tuy nhiên, trong mẫu mã 2, chúng tôi có hai chức năng:

template <typename T, typename> 
void func(T) { ... } 

template <typename T, typename> 
void func(T) { ... } 

Đó là giống nhau! Chúng tôi đang xác định cùng một chức năng hai lần - điều này không được phép, do đó lỗi. Đây là lỗi do cùng một lý do:

int foo(int x = 1) { return x; } 
int foo(int x = 2) { return x; } 

là lỗi.


Đây là lý do tại sao Xéo sử dụng (unconstructible) enum cho phép nếu để bắt đầu với - nó làm cho nó dễ dàng hơn để viết rời nhau hàm mẫu chuyên ngành kể từ khi bạn có thể làm điều đó với enum nhưng bạn không thể làm điều đó với loại.

Lưu ý rằng bạn có thể tương tự như chỉ làm tương tự với một cái gì đó giống như int:

template <class T, std::enable_if_t<std::is_integral<T>::value, int> = 0> 
void func(T) { ... } 

Nhưng điều này sẽ cho phép người dùng ác để cung cấp giá trị cho tham số đó (nó sẽ không quan trọng trong bối cảnh đặc biệt này nhưng nó có thể ở những người khác). Với detail::enabler, không có giá trị như vậy có thể được cung cấp.

+0

Vâng, tôi không có probelm với hai đoạn mã cuối cùng. Nhưng vấn đề là với vấn đề đầu tiên. Làm thế nào đến là tham số thứ hai trong cả hai đều không giống nhau? – mkmostafa

+1

Không chính xác khi nói rằng mẫu mã 1 hoạt động vì các hàm có chữ ký khác nhau. – user2296177

+0

@mkmostafa Tại sao chúng lại giống nhau? Họ trông khác với tôi. – Barry

2

Tôi đoán tôi đi đến một kết luận (có thể ai có thể xác minh hoặc cập nhật):

Trong mẫu 2:

Khi trình biên dịch gặp một cuộc gọi hàm và cố gắng khớp nó với một mẫu nó tìm thấy hai mẫu với các tham số typename Ttypename T2 (các giá trị mặc định không quan trọng, điều này là bình thường) và điều này tạo ra một sự mơ hồ.

Trong mẫu 1:

Khi trình biên dịch enounters một hàm gọi nó sẽ cố gắng để kết hợp nó với một trong hai mẫu, một trong số họ sẽ thành công vì vậy nó sẽ có typename Tdetail::enabler và người kia sẽ không có giá trị được xác định cho tham số mẫu thứ hai, do đó, sự mơ hồ sẽ bị xóa trong trường hợp này.

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