10

Sử dụng VC++ 2010, đưa ra sau đây:Tại sao trình biên dịch chọn chức năng mẫu này trên một chức năng không mẫu quá tải?

class Base { }; 
class Derived : public Base { }; 

template<class T> void foo(T& t); // A 
void foo(Base& base);    // B 

Derived d; 
foo(d);       // calls A 
foo(static_cast<Base&>(d));  // calls B 

Tôi muốn "B" được gọi là ở trên. Tôi có thể đạt được điều này với một dàn diễn viên để Base, nhưng tại sao điều này là cần thiết?

Tôi muốn chức năng mẫu được gọi cho tất cả các loại không có nguồn gốc từ Base (kiểu tích hợp, v.v.), nhưng tôi muốn quá tải mẫu không được gọi cho các loại bắt nguồn từ Base, mà không yêu cầu khách hàng để đúc một cách rõ ràng. Tôi cũng đã cố gắng làm quá tải một chuyên môn của mẫu, nhưng hành vi tương tự xảy ra trong trường hợp đó. Cách thành ngữ để có được những gì tôi đang tìm kiếm là gì?

Trả lời

12

Tất cả mọi thứ đều bằng nhau, các chức năng nontemplate được ưu tiên hơn các mẫu chức năng. Tuy nhiên, trong kịch bản của bạn, tất cả mọi thứ không bằng nhau: (A) là đối sánh chính xác với T = Derived, nhưng (B) yêu cầu chuyển đổi từ gốc sang cơ sở của đối số.

Bạn có thể làm việc xung quanh này đối với trường hợp cụ thể (như thế này) bằng cách sử dụng SFINAE (thất bại thay không phải là một lỗi) để ngăn chặn (A) không bị khởi tạo với một loại mà có nguồn gốc từ Base:

#include <type_traits> 
#include <utility> 

template <typename T> 
typename std::enable_if< 
    !std::is_base_of<Base, T>::value 
>::type foo(T& x) 
{ 
} 

void foo(Base& x) 
{ 
} 
+0

Đây có phải là điều C++ 11 hay nó hoạt động trong mã cũ hơn - chỉ hỏi vì nó tuyệt vời :)? –

+1

@ w00te: 'is_base_of' và' enable_if' được bao gồm trong Boost, C++ TR1 và C++ 11. Không yêu cầu chức năng C++ 11 cho chúng; bạn có thể định nghĩa chúng chỉ sử dụng các tính năng ngôn ngữ C++ 03. –

+0

@ w00te: bạn có thể làm điều đó trong C++ 03, nhưng bạn phải lấy 'enable_if' và' is_base_of' từ tăng (hoặc cuộn của riêng bạn, điều này sẽ dạy cho bạn một vài điều về C++). –

2

Phiên bản mẫu đang được chọn vì nó phù hợp hơn khi được gọi với đối số kiểu Derived hơn phiên bản quá tải. Bạn có thể sử dụng SFINAE để thả phiên bản mẫu từ độ phân giải quá tải sao cho phiên bản khác được chọn khi gọi với các đối số thuộc loại Base hoặc Derived.

#include <type_traits> 
#include <iostream> 

class Base { }; 
class Derived : public Base { }; 

template<class T> 
typename std::enable_if< 
    std::is_base_of<Base, T>::value == false 
>::type 
foo(T&) 
{ 
    std::cout << "template foo" << std::endl; 
} 


void foo(Base&) 
{ 
    std::cout << "non-template foo" << std::endl; 
} 


int main() 
{ 
    Derived d; 
    Base b; 

    foo(d); 
    foo(b); 
} 
+0

'is_same' không cần thiết ở đây. – Pubby

+0

@Pubby Cảm ơn, tôi sẽ khắc phục – Praetorian