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 int
T
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
}
cả hai đều phàn nàn không có 'foo' định nghĩa .... – UmNyobe
@UmNyobe Vâng, tôi biết, nó là cần thiết để in loại chỉ – FrozenHeart
@ 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