2016-05-22 12 views
23

Tôi có định nghĩa sau.Quá tải hàm mẫu trong C++

using namespace std; 

template <typename T> 
void foo(const T &s) { 
    cout << 1; 
} 

template <typename T> 
void foo(const T *s) { 
    cout << 2; 
} 

int main(int argc, const char * argv[]) { 
    char str[] = "ss"; 
    char *s = str; 
    foo(s); 

    return 0; 
} 

Sau đó nó ra

1 

Từ hiểu biết của tôi, cả hai phiên bản phải trải qua một sự chuyển đổi const. Sau đó, void foo(const T *s) là chuyên biệt hơn và cần được gọi. Tuy nhiên trình biên dịch đã chọn void foo(const T& s). Giải thích là gì?

+2

'const T & s' -> 'T const & s' ->' T = char * '->' char * const &' –

+1

tôi biên dịch nó và vẫn không thể tin được chọn 1 – bolov

Trả lời

15

Một số người đã chỉ ra rằng các thông số của các mẫu như được lựa chọn bởi trình biên dịch

void f(char * const&) 
void f(const char *); 

Ở đây, nhận thấy rằng trình biên dịch hy vọng một con trỏ đến char, char*, cho các chức năng đầu tiên và đó là một tham chiếu const . Nó có thể đến như là một sự ngạc nhiên, nếu bạn phát hiện ra rằng đối với trường hợp của bạn nó thích mẫu đầu tiên, nhưng đối với hai sau, nó sẽ thích thứ hai

template <typename T> 
void foo(const T& s) { 
    cout << 1; 
} 

template <typename T> 
void foo(T &s) { 
    cout << 2; 
} 

Vì vậy, tất nhiên, nó sẽ nhìn tại const đôi khi. Tại sao nó không trong trường hợp của bạn? Bởi vì nó sẽ chỉ xem xét const để tham khảo nếu hàm kia cũng có tham chiếu.

Trong trường hợp của bạn, từ char* đến const char* đó là chuyển đổi con trỏ, nhưng từ lvalue thành const lvalue, đó không thực sự là chuyển đổi. Việc thêm một const bởi tham chiếu const được bỏ qua bởi độ phân giải quá tải trừ khi cả hai hàm đều có tham số tham chiếu như trong trường hợp trên.

+1

những gì tôi trước đây viết trong câu trả lời về "chuyển đổi lvalue" là vô nghĩa đơn giản .. Tôi đã một nửa ngủ khi tôi đã viết câu trả lời này. –

+0

BTW trước http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1401 có một lưu ý hữu ích cho biết "Liên kết tham chiếu đến biểu thức tương thích với tham chiếu với trình độ bổ sung ảnh hưởng đến xếp hạng của một chuyển đổi tiêu chuẩn, xem 13.3.3.2 [over.ics.rank] và 8.5.3 [dcl.init.ref]. ". Tôi sẽ thích hơn nếu họ giữ câu này và viết lại. Lưu ý các từ ngữ "ảnh hưởng đến thứ hạng của một chuyển đổi tiêu chuẩn" .. đó không phải là ngẫu nhiên nhưng nhấn mạnh rằng nó chỉ ảnh hưởng đến việc xếp hạng, và không cấu thành một chuyển đổi trên riêng của mình. –

5

Lý do là vì s là một con trỏ không const, vì vậy int * const & thực sự là một kết hợp tốt hơn int const * vì nó không phải thêm const vào kiểu con trỏ.

Nếu s là const đủ điều kiện thì nó sẽ là đối sánh chính xác cho phiên bản T const *.

2

cả hai phiên bản đều phải trải qua chuyển đổi const.

Người đầu tiên không cần chuyển đổi. Đối với thông số mẫu const T& với đối số mẫu char *, T sẽ được khấu trừ là char* và sau đó loại tham số chức năng sẽ là char* const &, do đó, đó là đối sánh hoàn hảo. Đối số hàm sẽ được ràng buộc để tham chiếu đến const (T ->const T&, tức là char* đến char* const&), const đủ điều kiện không phải là chuyển đổi.

Đối với một trong 2, các mẫu tham số const T* với mẫu đối số char *, T sẽ được suy luận như char và sau đó là loại tham số chức năng sẽ const char*qualification conversion là cần thiết để chuyển đổi char*-const char*.

Từ nhận xét của bạn

Cả thói quen thêm ở mức độ thấp const để s.

Thứ nhất là thêm const vào s, nhưng số thứ hai thì không. Nó thêm const vào những gì s trỏ đến, không phải s chính nó. Đây là sự khác biệt.

+0

Anh ta có thể mong đợi rằng 'char * const &' cho một 'char *' sẽ cần một chuyển đổi const. Trực giác, đó không phải là quá xa để mong đợi, IMO. –

+1

Tại sao 'const T & s' bằng' T = char * 'sẽ đi tới' char * const & '? Tôi có nghĩa là tại sao vị trí của 'const' thay đổi. –

+0

@ JohannesSchaub-litb, vâng đó thực sự là những gì tôi đã mong đợi. Cả hai thường trình thêm const cấp thấp vào 's'. Nhưng tại sao chuyển đổi này không xảy ra ở đây? –

4

Tôi đã di chuyển const mà không thay đổi ngữ nghĩa của mã, chỉ để bạn không ngạc nhiên khi vị trí của const "thay đổi" sau này.

template <typename T> 
void foo(T const &s) { 
    cout << 1; 
} 

template <typename T> 
void foo(T const *s) { 
    cout << 2; 
} 

char *x = "x"; 
foo(x); 

Overload 1 sẽ phải suy Tchar* để loại s sẽ char * const & (tham chiếu đến con trỏ const để không const char). Tham chiếu như vậy có thể liên kết với loại đối số (char *; con trỏ tới không phải là const char) mà không có bất kỳ chuyển đổi nào.

quá tải 2 sẽ phải suy Tchar để loại s sẽ char const * (con trỏ đến const char). Điều này phát sinh chuyển đổi đủ điều kiện từ loại đối số (char *; con trỏ tới không phải là const char) thành loại tham số (char const *; con trỏ đến const char).

Một ví dụ minh họa nhiều nguyên tắc trong tình trạng quá tải 1 như sau:

int n = 42; 
int const &i = n; 

Ràng buộc một tham chiếu const để một tổ chức phi const không liên quan đến chuyển đổi, bởi vì đó là tài liệu tham khảo có thêm trình độ , đó không phải là tiêu chuẩn được thêm vào thực thể để đối sánh nó với loại tham chiếu.

1

Lý do là vì khi đối số là một con trỏ, bạn phải vượt qua trong một con trỏ .. Như thế này:

// Example program 
#include <iostream> 
#include <string> 

using std::cout; 
using std::endl; 

void foo(int *a) 
{ 
    cout << *a << endl; 
} 

int main() 
{ 
    int a = 5; 
    foo(&a); 
} 

Nhưng nếu lập luận của bạn là một refrence bạn chỉ có thể vượt qua các tham số vì nó là như thế này:

// Example program 
#include <iostream> 
#include <string> 

using std::cout; 
using std::endl; 

void foo(int &a) 
{ 
    cout << a << endl; 
} 

int main() 
{ 
    int a = 5; 
    foo(a); 
}