2013-03-19 26 views
12

Giả sử chúng ta có đoạn mã sau:Chuỗi chữ trong mẫu - hành vi khác nhau của trình biên dịch

template <typename T> 
void foo(const T&); 

int main() 
{ 
    foo("str"); 
} 

Demonstration

gcc 4.7.2, Clang 3.2, icc 13.0.1

tham chiếu không xác định đối với `void foo < char [4]> (char const (&)) [4])'

MSVC-11,0

chưa được giải quyết bên ngoài biểu tượng "void __cdecl foo < const char [4]> (char const (&) [4]) "(?? $ foo @ $$ $$ BY03 CBD @@ YAXAAY03 $$ CBD @ Z)

Thông báo char[4] trong đầu ra đầu tiên và char const[4] trong o thứ hai utput.

Tại sao? Và ai đúng? Bạn có thể trích dẫn tiêu chuẩn không?

+7

cả hai đều phàn nàn không có 'foo' định nghĩa .... – UmNyobe

+0

@UmNyobe Vâng, tôi biết, nó là cần thiết để in loại chỉ – FrozenHeart

+0

@ UmNyobe tôi có nghĩa là những gì loại nên được trong trường hợp này - char [4] hoặc char const [4]? – FrozenHeart

Trả lời

1

GCC là chính xác; rằng const trong danh sách đối số mẫu của VS không được ở đó:

[C++11: 14.8.2/3]: Sau khi thực hiện thay thế này, các điều chỉnh loại tham số chức năng được mô tả trong 8.3.5 được thực hiện. [Ví dụ: Loại thông số “void()(const int, int[5])” trở thành “void(*)(int,int*)”. —end example][Lưu ý: Bộ định tính cấp cao nhất trong khai báo tham số hàm không ảnh hưởng đến chức năng loại nhưng vẫn ảnh hưởng đến loại biến tham số chức năng trong hàm. -end note][Ví dụ:

template <class T> void f(T t); 
template <class X> void g(const X x); 
template <class Z> void h(Z, Z*); 

int main() { 
    // #1: function type is f(int), t is non const 
    f<int>(1); 

    // #2: function type is f(int), t is const 
    f<const int>(1); 

    // #3: function type is g(int), x is const 
    g<int>(1); 

    // #4: function type is g(int), x is const 
    g<const int>(1); 

    // #5: function type is h(int, const int*) 
    h<const int>(1,0); 
} 

-end dụ]

(Ví dụ 4 là một thích hợp.)

[C++11: 14.8.2/5]:Các kết quả loại chức năng thay thế và điều chỉnh được sử dụng làm loại của f mẫu unction để khấu trừ đối số mẫu.[..]

Cũng có thể có liên quan:

Suy luận đối số mẫu từ một cuộc gọi chức năng
[C++11: 14.8.2.1/2]: Nếu P không phải là một loại tài liệu tham khảo:

  • Nếu A là một loại mảng , loại con trỏ được tạo ra bởi phép chuyển đổi tiêu chuẩn mảng thành con trỏ (4.2) được sử dụng thay cho A để khấu trừ loại; nếu không,
  • Nếu A là loại chức năng, loại con trỏ được tạo bởi chuyển đổi tiêu chuẩn chức năng thành con trỏ (4.3) được sử dụng thay cho số A để khấu trừ loại; cách khác,
  • Nếu A là một loại cv-đủ điều kiện, phía trên mức cv-vòng loại loại A ‘s được bỏ qua cho loại trừ
+1

Chúng ta đều biết điều này, nhưng nó áp dụng như thế nào cho trường hợp được đề cập? –

+0

@JamesKanze: 'const' không phải là một phần của kiểu hàm và kiểu hàm được sử dụng để trích mẫu đối số. Do đó, 'T' trong trường hợp này sẽ không phải là' const', nhưng trong ví dụ của OP, MSVC đã làm như vậy. –

+0

Tôi nghĩ tôi có thể thiếu một vài bước. :(Tôi nghĩ rằng tôi sẽ ném nó ra khỏi đó, mặc dù –

5

GCC là đúng.

Hãy bắt đầu từ một ví dụ đơn giản hơn một chút, và sau đó chứng minh rằng ví dụ ban đầu theo cùng một khuôn mẫu:

template<typename T> 
void bar(T const&) 
{ 
    // Shall not fire 
    static_assert(std::is_same<T, int>::value, "Error!"); 
} 

int main() 
{ 
    int x = 0; 
    bar(x); // 1 - Assertion won't fire 

    int const y = 0; 
    bar(y); // 2 - Assertion won't fire 
} 

gì đang xảy ra ở đây? Trước hết, theo § 14.8.2.1/3:

[...] Nếu P là kiểu tham chiếu, kiểu P được dùng để khấu trừ kiểu. [...]

này có nghĩa là loại trừ sẽ cố gắng để phù hợp với T const chống int (trong trường hợp 1) và chống lại int const (trong trường hợp 2). Trong trường hợp thứ hai, thay thế int cho T sẽ mang lại kết quả khớp hoàn hảo, vì vậy thật dễ dàng; trong trường hợp đầu tiên, chúng tôi có const đang trên đường để có một trận đấu hoàn hảo. Nhưng đây là nơi § 14.8.2.1/4 đi vào chơi:

[...] Nếu P ban đầu là một loại tài liệu tham khảo, những suy luận A (tức là, loại được gọi bằng các tài liệu tham khảo) có thể hơn cv-trình độ hơn A. chuyển [...]

Ở đây, thay thế cho intT cho chúng ta một suy luận int const, mà là nhiều cv-có trình độ hơn int (loại của đối số x). Nhưng đó là chấp nhận được vì § 14.8.2.1/4 ở trên, vì vậy ngay cả trong trường hợp này, T được suy ra là int.

Bây giờ hãy giải quyết ví dụ ban đầu của bạn (chỉ cần một chút điều chỉnh, nhưng chúng tôi sẽ nhận được phiên bản gốc cuối cùng):

template<typename T> 
void bar(T const&) 
{ 
    // Does not fire in GCC, fires in VC11. Who's right? 
    static_assert(std::is_same<T, char[4]>::value, "Error!"); 
} 

int main() 
{ 
    char x[] = "foo"; 
    bar(x); 

    char const y[] = "foo"; 
    bar(y); 
} 

Ngoài thực tế là tôi đã thay thế int với char [], đây là ví dụ và tôi ví dụ đầu tiên là giống nhau về cấu trúc. Để xem tại sao tương đương này nắm giữ, hãy xem xét sự khẳng định dưới đây (mà không bắn vào bất kỳ trình biên dịch, như mong đợi):

// Does not fire 
static_assert(
    std::is_same< 
     std::add_const<char [4]>::type, 
     char const[4] 
    >::value, "Error"); 

C++ 11 nhiệm vụ tiêu chuẩn hành vi này tại Khoản 3.9.3/2 :

Bất kỳ loại vòng loại c nào áp dụng cho loại mảng đều ảnh hưởng đến loại phần tử mảng, không phải kiểu mảng (8.3.4).

Đoạn 8.3.4/1 cũng quy định cụ thể:

[...] Bất kỳ loại dạng “mảng cv-vòng-seq của NT” được điều chỉnh để “mảng của N cv -qualifier-seq T ”, và tương tự cho“ mảng không xác định ràng buộc của T ”. Tùy chọn thuộc tính-specifier-seq áp dụng cho mảng. [Ví dụ:

typedef int A[5], AA[2][3]; 
typedef const A CA; // type is “array of 5 const int” 
typedef const AA CAA; // type is “array of 2 array of 3 const int” 

-end dụ] [Chú ý: Một “mảng của N cv-vòng-seq T” có kiểu cv-đủ tiêu chuẩn; xem 3.9.3. —thêm ghi chú]

Vì bây giờ rõ ràng hai ví dụ thể hiện cùng một mẫu, nên áp dụng cùng một logic. Và điều đó sẽ dẫn chúng ta qua con đường lý luận rất giống nhau.

Khi thực hiện khấu trừ loại, T const được đối sánh với char[4] trong trường hợp đầu tiên và chống lại char const[4] trong trường hợp thứ hai.

Trong trường hợp thứ hai, T = char[4] mang lại kết quả phù hợp hoàn hảo, vì T const trở thành char const[4] sau khi thay thế. Trong trường hợp đầu tiên, khoản khấu trừ A lại một lần nữa cv đủ điều kiện so với A ban đầu, trong đó thay thế char[4] cho T sản lượng char const[4]. Nhưng sau đó một lần nữa, được cho phép trước ngày 14.8.2.1/4, do đó, T nên được suy ra là char[4].

Cuối cùng, quay lại ví dụ ban đầu của bạn. Kể từ khi chuỗi chữ "str" cũng có kiểu char const[4], T nên được suy luận là char [4], có nghĩa là GCC là đúng:

template<typename T> 
void foo(T const&) 
{ 
    // Shall not fire 
    static_assert(std::is_same<T, char[4]>::value, "Error!"); 
} 

int main() 
{ 
    foo("str"); // Shall not trigger the assertion 
} 
+0

Nhưng tại sao 'std :: add_const :: type' giống như' char const [4] '? Điều đó dường như không rõ ràng - đầu tiên là một mảng const của 4 ký tự, trong khi thứ hai là một mảng gồm 4 ký tự const. – interjay

+0

@interjay: Xem 3.9.3/2 của tiêu chuẩn C++ 11: "[...] Bất kỳ loại vòng loại cv nào áp dụng cho loại mảng đều ảnh hưởng đến loại phần tử mảng, không phải kiểu mảng (8.3.4)." –

+0

@interjay: Tôi đã mở rộng một chút về điều này trong lần cập nhật cuối cùng cho câu trả lời của tôi –

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