Tại sao chuỗi :: so sánh trả về một int?


102

Tại sao string::comparetrả về một intthay vì một loại nhỏ hơn như shorthoặc char? Sự hiểu biết của tôi là phương thức này chỉ trả về -1, 0 hoặc 1.

Phần thứ hai, nếu tôi thiết kế một phương thức so sánh để so sánh hai đối tượng cùng loại Foovà tôi chỉ muốn trả về -1, 0 hoặc 1, thì việc sử dụng shorthay charnói chung là một ý tưởng hay?

CHỈNH SỬA: Tôi đã được sửa, string::comparekhông trả về -1, 0 hoặc 1, nó thực tế trả về giá trị> 0, <0 hoặc 0. Cảm ơn vì đã giúp tôi theo dõi.

Có vẻ như câu trả lời là đại khái, không có lý do gì để trả về một kiểu nhỏ hơn intvì các giá trị trả về là "rvalues" và những "rvalues" đó không được lợi khi nhỏ hơn kiểu int (4 byte). Ngoài ra, nhiều người đã chỉ ra rằng dù sao thì các thanh ghi của hầu hết các hệ thống đều có kích thước int, vì các thanh ghi này sẽ được lấp đầy cho dù bạn cung cấp cho chúng giá trị 1, 2 hoặc 4 byte, không có lợi ích thực sự nào khi trả về giá trị nhỏ hơn.

CHỈNH SỬA 2: Trên thực tế, có vẻ như có thể có thêm chi phí xử lý khi sử dụng các kiểu dữ liệu nhỏ hơn như căn chỉnh, tạo mặt nạ, v.v. Sự đồng thuận chung là các kiểu dữ liệu nhỏ hơn tồn tại để tiết kiệm trên bộ nhớ khi làm việc với nhiều dữ liệu, như trong trường hợp của một mảng.

Hôm nay đã học được điều gì đó, một lần nữa cảm ơn các bạn!


Tôi nghĩ sẽ tốt hơn nếu có một loại cụ thể hơn có thể được sử dụng cho việc này. Một chỉ chứa -1, 0 và 1 theo kiểu Ada95.
Sachin Kainth

23
Các tài liệu cho string::compare()bạn liên kết đến nêu rõ giá trị trả về là <0, 0, và> 0 -not- -1, 0 và 1.
Captain Obvlious

6
Lợi thế của việc sử dụng shorthoặc charthay thế là intgì? Hầu hết các kiến ​​trúc sẽ lưu trữ giá trị trả về của một hàm trong một thanh ghi, và một intsẽ phù hợp với một thanh ghi cũng như một shorthoặc char. Và việc sử dụng charcho các kiểu số luôn là một ý tưởng tồi, đặc biệt khi bạn cần đảm bảo các giá trị đã ký được xử lý chính xác.
Cody Grey

7
Thuyền trưởng Obvlious, tên và nhận xét của bạn ... Thật vô giá.
Cody Smith

2
Sử dụng charsẽ là một ý tưởng tồi, vì việc kiểm tra mã cho giá trị trả về nếu nó nhỏ hơn 0 sẽ không thành công trên các nền tảng charchưa được ký.
milleniumbug

Câu trả lời:


113

Đầu tiên, đặc tả là nó sẽ trả về giá trị nhỏ hơn, bằng hoặc lớn hơn 0, không nhất thiết phải -1hoặc 1. Thứ hai, giá trị trả về là giá trị rvalues, có thể được khuyến mãi tích hợp, vì vậy không có ích gì khi trả về bất kỳ thứ gì nhỏ hơn.

Trong C ++ (như trong C), mọi biểu thức đều là một giá trị hoặc một giá trị. Về mặt lịch sử, các thuật ngữ đề cập đến thực tế là các giá trị xuất hiện ở bên trái của một phép gán, trong đó các giá trị chỉ có thể xuất hiện ở bên phải. Ngày nay, một phép gần đúng đơn giản cho các kiểu không phải lớp là giá trị có địa chỉ trong bộ nhớ, giá trị thì không. Do đó, bạn không thể lấy địa chỉ của một rvalue và các định nghĩa cv (điều kiện "truy cập") không áp dụng. Trong thuật ngữ C ++, một giá trị không có kiểu lớp là một giá trị thuần túy, không phải là một đối tượng. Giá trị trả về của một hàm là một rvalue, trừ khi nó có kiểu tham chiếu. (Ví dụ, các kiểu không phải lớp nằm trong một thanh ghi sẽ luôn được trả về trong một thanh ghi, thay vì trong bộ nhớ.)

Đối với các loại lớp, các vấn đề phức tạp hơn một chút, do thực tế là bạn có thể gọi các hàm thành viên trên một rvalue. Điều này có nghĩa là trên thực tế, các giá trị phải có địa chỉ cho this con trỏ và có thể đủ tiêu chuẩn cv, vì tiêu chuẩn cv đóng một vai trò trong việc giải quyết quá tải. Cuối cùng, C ++ 11 giới thiệu một số điểm khác biệt mới, để hỗ trợ các tham chiếu rvalue; những điều này cũng chủ yếu áp dụng cho các loại lớp.

Thăng hạng tích phân đề cập đến thực tế là khi các loại tích phân nhỏ hơn an intđược sử dụng làm giá trị trong một biểu thức, trong hầu hết các ngữ cảnh, chúng sẽ được thăng cấp int. Vì vậy, ngay cả khi tôi có một biến được khai báo short a, b;, trong biểu thức a + b, cả hai abđều được thăng cấp inttrước khi phép cộng xảy ra. Tương tự, nếu tôi viết a < 0, phép so sánh được thực hiện trên giá trị của a, được chuyển đổi thành an int. Trong thực tế, có rất ít trường hợp mà điều này tạo ra sự khác biệt, ít nhất là trên các máy bổ sung của 2 trong đó số học nguyên kết thúc (tức là tất cả trừ một số ngoại lệ, ngày nay - tôi nghĩ các máy tính lớn của Unisys là ngoại lệ duy nhất còn lại). Tuy nhiên, ngay cả trên các máy phổ biến hơn:

short a = 1;
std::cout << sizeof( a ) << std::endl;
std::cout << sizeof( a + 0 ) << std::endl;

nên cho kết quả khác nhau: kết quả đầu tiên là tương đương với sizeof( short ), thứ hai sizeof( int )(vì tích phân thăng hạng).

Hai vấn đề này về mặt hình thức là trực giao; giá trị và giá trị không liên quan gì đến việc quảng bá tích hợp. Ngoại trừ ... khuyến mại tích hợp chỉ áp dụng cho các giá trị và hầu hết (nhưng không phải tất cả) các trường hợp mà bạn sử dụng một giá trị sẽ dẫn đến khuyến mại tích hợp. Vì lý do này, thực sự không có lý do gì để trả về một giá trị số trong một thứ gì đó nhỏ hơn int. Thậm chí có một lý do rất tốt để không trả lại nó dưới dạng một loại ký tự. Các toán tử bị quá tải, giống như <<, thường hoạt động khác nhau đối với các loại ký tự, vì vậy bạn chỉ muốn trả về các ký tự dưới dạng các loại ký tự. (Bạn có thể so sánh sự khác biệt:

char f() { return 'a'; }
std::cout << f() << std::endl;      //  displays "a"
std::cout << f() + 0 << std::endl;  //  displays "97" on my machine

Sự khác biệt là trong trường hợp thứ hai, phép cộng đã gây ra sự thăng hạng tích phân xảy ra, dẫn <<đến việc chọn quá tải khác nhau .


46
Sẽ rất tốt nếu bạn có thể giải thích thêm về return values are rvalues, subject to integral promotioncâu trả lời của mình.
Alvin Wong

"giá trị trả về là rvalues ... do đó, không có điểm nào đó trong trở lại bất cứ điều gì nhỏ" Like It
Masoud

1
@AlvinWong: Xem câu trả lời cho Tại sao ký tự C là ints thay vì ký tự? để biết thêm thông tin cơ bản.
Jesse Good

Tôi ước gì tôi có thể +1 điều này một lần nữa, sau khi phần giải thích tuyệt vời mà chỉnh sửa của bạn được thêm vào.
Cody Grey

Nếu nó là signed chargì? Nó sẽ hoạt động giống như một chữ ký char, hay nó sẽ là một loại khác?
user541686

41

Có chủ ý là nó không trả về -1, 0 hoặc 1.

Nó cho phép (lưu ý điều này không dành cho chuỗi, nhưng nó áp dụng tương tự cho chuỗi)

int compare(int *a, int *b)
{
   return *a - *b;
}

mà ít cồng kềnh hơn nhiều so với:

int compare(int *a, int *b)
{
   if (*a == *b) return 0;
   if (*a > *b) return 1;
   return -1;
}

đó là những gì bạn phải làm [hoặc điều gì đó dọc theo những dòng đó] nếu bạn phải trả về -1, 0 hoặc 1.

Và nó cũng hoạt động cho các loại phức tạp hơn:

class Date
{
    int year;
    int month;
    int day;
}

int compare(const Date &a, const Date &b)
{
   if (a.year != b.year) return a.year - b.year;
   if (a.month != b.month) return a.month - b.month;
   return a.day - b.day;
}

Trong trường hợp chuỗi, chúng ta có thể làm điều này:

int compare(const std::string& a, const std::string& b)
{
   int len = min(a.length(), b.length());

   for(int i = 0; i < len; i++)
   {
      if (a[i] != b[i]) return a[i] - b[i];
   }
   // We only get here if the string is equal all the way to one of them
   // ends. If the length isn't equal, "longest" wins. 
   return a.length() - b.length();
}

8
compareHàm đầu tiên của bạn có vấn đề với tràn mà (may mắn thay) không áp dụng như nhau nếu nó mất char*charnhỏ hơn int. Ví dụ, nếu *aMAX_INT*b-1sau đó *a - *blà UB, nhưng nếu chọn nhóm thực hiện để xác định hành vi của nó thì kết quả gần như chắc chắn là tiêu cực.
Steve Jessop

1
Vấn đề với ví dụ cuối cùng của bạn: length()trả về a size_t, có thể lớn hơn int
F'x

Vâng, đó có thể là vấn đề nếu chuỗi của bạn dài hơn 2GB. Tôi đã thực hiện các chuỗi dài 1GB như một trường hợp thử nghiệm để lưu trữ mọi thứ trong một lần. Nhưng chắc chắn rằng, ai đó xử lý một chuỗi chứa MPEG được mã hóa dưới dạng Base64 hoặc một số tương tự cũng có thể gặp phải vấn đề đó ...
Mats Petersson

@MatsPetersson nó là một vấn đề cơ bản hơn, bởi vì câu hỏi là "tại sao nó trả về một int?"
F'x

Chà, tôi chắc chắn đó là lý do quá khích - ý tôi là lý do lịch sử - và có lẽ vì vậy nó tương thích với strcmp / memcmp và các phép toán kiểu so sánh khác.
Mats Petersson

25

int thường là (có nghĩa là trên hầu hết các phần cứng hiện đại) một số nguyên có cùng kích thước với bus hệ thống và / hoặc các thanh ghi cpu, được gọi là từ máy. Do đó int thường được truyền nhanh hơn các kiểu nhỏ hơn, bởi vì nó không yêu cầu căn chỉnh, tạo mặt nạ và các thao tác khác.

Các loại nhỏ hơn tồn tại chủ yếu để cho phép tối ưu hóa việc sử dụng RAM cho các mảng và cấu trúc. Trong hầu hết các trường hợp, họ đánh đổi một vài chu kỳ CPU (dưới dạng các hoạt động thay đổi) để sử dụng RAM tốt hơn.

Trừ khi bạn cần phải thực thi giá trị trả về của mình thành một số có dấu hoặc không dấu có kích thước bằng centain (char, short…) thì tốt hơn hết bạn nên sử dụng int, đó là lý do tại sao thư viện tiêu chuẩn lại làm như vậy.


Một cách tuyệt vời để giải thích khía cạnh phần cứng của mọi thứ theo cách có lý.
Ogre Psalm33

10

Đó là một C-ism.

Khi C yêu cầu các comparehàm -type, chúng luôn trả về một int. C ++ chỉ tiến hành điều đó (thật không may).

Tuy nhiên, trả về một intcách thực tế có lẽ là cách nhanh nhất, vì nó nói chung là kích thước của các thanh ghi của hệ thống đang sử dụng. (Cố tình mơ hồ.)


1
Trên thực tế shortcharcó thể áp đặt các hình phạt về hiệu suất, ví dụ: 255+7có một giá trị khác cho a charvà an intvì vậy, một triển khai đúng không nhất thiết phải lưu trữ một charnơi intcó thể đi mà không cần quan tâm đến việc chuyển giao ngữ nghĩa của nó. Các trình biên dịch sẽ không nhất thiết phải tối ưu hóa sự kém hiệu quả mà điều này gây ra.
Jack Aidley

10

Phương thức không thực sự trả về một số nguyên trong tập hợp { -1, 0, 1 }; nó thực sự có thể là bất kỳ giá trị tích phân nào .

Tại sao? Lý do chính mà tôi có thể nghĩ ra là đó intđược cho là giá trị "kích thước tự nhiên" cho kiến ​​trúc; các thao tác trên các giá trị có kích thước này thường nhanh hơn ít nhất (và trong nhiều trường hợp là nhanh hơn) so với các thao tác trên các giá trị nhỏ hơn hoặc lớn hơn. Vì vậy, đây là một trường hợp cho phép việc triển khai đủ chậm để sử dụng bất cứ thứ gì nhanh nhất.


4

Nếu tôi thiết kế một phương pháp so sánh để so sánh hai đối tượng kiểu Foo và tôi chỉ muốn trả về -1, 0 hoặc 1, thì việc sử dụng short hoặc char nói chung có phải là một ý kiến ​​hay không?

Nó sẽ là ý tưởng ok. Một cách tốt hơn sẽ là trả về bool (nếu chỉ muốn so sánh nếu bằng nhau) hoặc enum (để biết thêm thông tin):

enum class MyResult
{
  EQUAL,
  LESS,
  GREATER
};

MyResult AreEqual( const Foo &foo1, const Foo & foo2 )
{
  // calculate and return result
}

3
"Nó sẽ là ý tưởng ok". Bạn có lý do cho điều đó không?
jrok

4

Giả sử một số người đang thay đổi mã từ C sang C ++. Họ quyết định thay thế strcmpthành string::compare.

Kể từ khi strcmptrả lại int, nó dễ dàng hơn để string::comparetrả lại intnhư một món quà.


2

Có lẽ để làm cho nó hoạt động giống như strcmpvậy cũng có tập hợp các giá trị trả về này . Nếu bạn muốn chuyển mã, có lẽ sẽ trực quan hơn nếu có các thay thế phân tách càng gần càng tốt.

Ngoài ra, giá trị trả về không chỉ -1, 0hoặc 1<0, 0hoặc >0.

Ngoài ra, như đã đề cập vì lợi nhuận có thể được khuyến mãi tích hợp nên không có ý nghĩa gì khi làm cho nó nhỏ hơn.


-1

bởi vì giá trị trả về kiểu boolean chỉ có thể là hai giá trị có thể có (true, false) và một hàm so sánh có thể trả về ba giá trị có thể có (nhỏ hơn, bằng, lớn hơn).

Cập nhật

Mặc dù chắc chắn có thể trả về một short có chữ ký, nhưng nếu bạn thực sự muốn triển khai hàm so sánh của riêng mình, bạn có thể trả về một giá trị nibble hoặc struct với hai boolean.


7
Không nơi nào trong câu hỏi nói gì về việc trả về kiểu Boolean. Trên thực tế, anh ấy đặc biệt đề xuất shortcharnhư những lựa chọn thay thế int.
Cody Grey
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.