Tôi đã gặp phải một số hành vi kỳ lạ khi sử dụng các đặc điểm kiểu C ++ và đã thu hẹp vấn đề của mình xuống thành vấn đề nhỏ kỳ quặc này mà tôi sẽ đưa ra rất nhiều lời giải thích vì tôi không muốn để ngỏ bất kỳ điều gì để hiểu sai.
Giả sử bạn có một chương trình như vậy:
#include <iostream>
#include <cstdint>
template <typename T>
bool is_int64() { return false; }
template <>
bool is_int64<int64_t>() { return true; }
int main()
{
std::cout << "int:\t" << is_int64<int>() << std::endl;
std::cout << "int64_t:\t" << is_int64<int64_t>() << std::endl;
std::cout << "long int:\t" << is_int64<long int>() << std::endl;
std::cout << "long long int:\t" << is_int64<long long int>() << std::endl;
return 0;
}
Trong cả biên dịch 32 bit với GCC (và với MSVC 32 và 64 bit), kết quả đầu ra của chương trình sẽ là:
int: 0
int64_t: 1
long int: 0
long long int: 1
Tuy nhiên, chương trình tạo ra từ biên dịch GCC 64-bit sẽ xuất ra:
int: 0
int64_t: 1
long int: 1
long long int: 0
Điều này thật kỳ lạ, vì đây long long int
là một số nguyên 64-bit có dấu và đối với mọi ý định và mục đích, giống hệt với kiểu long int
và int64_t
, vì vậy về mặt logic int64_t
, long int
và long long int
sẽ là kiểu tương đương - hợp ngữ được tạo ra khi sử dụng những kiểu này là giống hệt nhau. Một cái nhìn stdint.h
cho tôi biết tại sao:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
Trong một biên dịch 64-bit, int64_t
là long int
, không phải là một long long int
(rõ ràng).
Cách khắc phục tình trạng này khá dễ dàng:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Nhưng điều này thật khủng khiếp và không mở rộng quy mô tốt (các chức năng thực tế của chất uint64_t
, v.v.). Vì vậy, câu hỏi của tôi là: Có cách nào để nói với trình biên dịch rằng a long long int
cũng là a int64_t
, giống như long int
là không?
Suy nghĩ ban đầu của tôi là điều này là không thể, do cách định nghĩa kiểu C / C ++ hoạt động. Không có cách nào để chỉ định sự tương đương về kiểu của các kiểu dữ liệu cơ bản cho trình biên dịch, vì đó là công việc của trình biên dịch (và cho phép điều đó có thể phá vỡ nhiều thứ) và typedef
chỉ đi theo một chiều.
Tôi cũng không quá lo lắng về việc nhận được câu trả lời ở đây, vì đây là một trường hợp siêu phức tạp mà tôi không nghi ngờ có ai đó sẽ quan tâm đến khi các ví dụ không quá khủng khiếp (điều đó có nghĩa là đây phải là wiki cộng đồng?) .
Nối : Lý do tại sao tôi đang sử dụng chuyên môn hóa từng phần mẫu thay vì ví dụ dễ dàng hơn như:
void go(int64_t) { }
int main()
{
long long int x = 2;
go(x);
return 0;
}
là ví dụ đã nói đó sẽ vẫn biên dịch, vì long long int
hoàn toàn có thể chuyển đổi thành an int64_t
.
Nối : Câu trả lời duy nhất cho đến nay giả định rằng tôi muốn biết một loại có phải là 64-bit hay không. Tôi không muốn đánh lừa mọi người nghĩ rằng tôi quan tâm đến điều đó và có lẽ nên cung cấp thêm các ví dụ về nơi biểu hiện của vấn đề này.
template <typename T>
struct some_type_trait : boost::false_type { };
template <>
struct some_type_trait<int64_t> : boost::true_type { };
Trong ví dụ này, some_type_trait<long int>
sẽ là a boost::true_type
, nhưng some_type_trait<long long int>
sẽ không phải. Mặc dù điều này có ý nghĩa trong ý tưởng của C ++ về các loại, nhưng nó không được mong muốn.
Một ví dụ khác là sử dụng một định tính như same_type
(khá phổ biến để sử dụng trong C ++ 0x Khái niệm):
template <typename T>
void same_type(T, T) { }
void foo()
{
long int x;
long long int y;
same_type(x, y);
}
Ví dụ đó không thể biên dịch, vì C ++ (chính xác) thấy rằng các kiểu khác nhau. g ++ sẽ không biên dịch được với lỗi như: không có lệnh gọi hàm phù hợp same_type(long int&, long long int&)
.
Tôi muốn nhấn mạnh rằng tôi hiểu tại sao điều này lại xảy ra, nhưng tôi đang tìm kiếm một giải pháp thay thế không buộc tôi phải lặp lại mã ở khắp nơi.
sizeof
từng loại không? Có lẽ trình biên dịch đang xử lý kích thướclong long int
khác nhau.