为什么 string::compare
返回 int
而不是像 short
或 char
这样的较小类型?我的理解是这个方法只返回-1、0或1。
第二部分,如果我要设计一个比较方法来比较两个 Foo
类型的对象并且我只想返回 -1、0 或 1,那么使用 short
或 char
通常是个好主意吗?
编辑:我已得到纠正,string::compare
不返回 -1、0 或 1,它实际上返回值 >0、<0 或 0。谢谢你们让我保持一致。
似乎答案是粗略的,没有理由返回小于 int
的类型,因为返回值是“右值”,而那些“右值”不会从小于 int 类型(4 字节)中受益。此外,许多人指出,大多数系统的寄存器可能无论如何都会有 int
的大小,因为无论你给它们一个 1、2 还是 4 字节的值,这些寄存器都会被填充,没有真正的优势返回一个较小的值。
编辑 2:事实上,当使用较小的数据类型(如对齐、屏蔽等)时,看起来可能会有额外的处理开销。普遍的共识是,在处理大量数据时,存在较小的数据类型是为了节省内存,如数组的情况。
今天学到了一些东西,再次感谢大家!
string::compare()
的文档清楚地说明了返回值是 <0, 0, and >0 -not- -1, 0 and 1。
short
或 char
而不是 int
有什么好处?大多数体系结构会将函数的返回值存储在寄存器中,int
与 short
或 char
一样适合寄存器。并且对数字类型使用 char
总是一个坏主意,尤其是当您需要保证正确处理有符号值时。
char
是个坏主意,因为如果返回值小于零,代码检查将在 char
未签名的平台上失败。
首先,规范是它将返回一个小于、等于或大于 0
的值,不一定是 -1
或 1
。其次,返回值是右值,受积分提升的影响,因此返回更小的值是没有意义的。
在 C++ 中(就像在 C 中一样),每个表达式都是右值或左值。从历史上看,这些术语指的是左值出现在赋值的左侧,而右值只能出现在右侧。今天,非类类型的一个简单近似是左值在内存中有一个地址,而右值没有。因此,您不能获取右值的地址,并且 cv 限定符(条件“访问”)不适用。在 C++ 术语中,没有类类型的右值是纯值,而不是对象。函数的返回值是一个右值,除非它具有引用类型。 (例如,适合寄存器的非类类型几乎总是在寄存器中返回,而不是在内存中。)
对于类类型,问题有点复杂,因为您可以在右值上调用成员函数。这意味着右值实际上必须具有 this
指针的地址,并且可以是 cv 限定的,因为 cv 限定在重载解析中起作用。最后,C++11 引入了几个新的区别,以支持右值引用;这些也主要适用于类类型。
整数提升是指当小于 int
的整数类型用作表达式中的右值时,在大多数情况下,它们将被提升为 int
。因此,即使我有一个声明为 short a, b;
的变量,在表达式 a + b
中,a
和 b
在加法发生之前都会被提升为 int
。同样,如果我写 a < 0
,则对 a
的值进行比较,转换为 int
。在实践中,很少有这样的情况会产生影响,至少在整数算术换行的 2 补码机器上(即,除了极少数外来机器,今天,我认为 Unisys 大型机是唯一的例外)。尽管如此,即使在更常见的机器上:
short a = 1;
std::cout << sizeof( a ) << std::endl;
std::cout << sizeof( a + 0 ) << std::endl;
应该给出不同的结果:第一个是 sizeof( short )
,第二个是 sizeof( int )
(因为积分提升)。
这两个问题在形式上是正交的;右值和左值与积分提升无关。 除了...积分提升仅适用于右值,并且大多数(但不是全部)使用右值的情况将导致积分提升。因此,实际上没有理由返回小于 int
的数值。甚至有一个很好的理由不将其作为字符类型返回。重载的运算符,如 <<
,对于字符类型通常表现不同,因此您只想将字符作为字符类型返回。 (您可以比较差异:
char f() { return 'a'; }
std::cout << f() << std::endl; // displays "a"
std::cout << f() + 0 << std::endl; // displays "97" on my machine
不同之处在于,在第二种情况下,加法导致发生积分提升,从而导致选择不同的 <<
重载。
故意不返回 -1、0 或 1。
它允许(注意这不适用于字符串,但它同样适用于字符串)
int compare(int *a, int *b)
{
return *a - *b;
}
这比:
int compare(int *a, int *b)
{
if (*a == *b) return 0;
if (*a > *b) return 1;
return -1;
}
如果您必须返回 -1、0 或 1,这就是您必须做的 [或类似的事情]。
它也适用于更复杂的类型:
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;
}
在字符串的情况下,我们可以这样做:
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();
}
compare
函数存在溢出问题,如果它需要 char*
并且 char
小于 int
,则(幸运的是)不会同样适用。例如,如果 *a
是 MAX_INT
而 *b
是 -1
那么 *a - *b
是 UB,但如果实现选择定义其行为,那么结果几乎肯定是否定的。
length()
返回一个 size_t
,它可能大于 int
...
int 通常(在大多数现代硬件上意味着)与系统总线和/或 cpu 寄存器大小相同的整数,即所谓的机器字。因此 int 通常比较小的类型传递得更快,因为它不需要对齐、屏蔽和其他操作。
存在较小的类型主要是为了允许优化数组和结构的 RAM 使用。在大多数情况下,它们会交换几个 CPU 周期(以对齐操作的形式)以获得更好的 RAM 使用率。
除非你需要强制你的返回值是一个百分号大小的有符号或无符号数(char,short...),否则你最好使用 int,这就是标准库这样做的原因。
这是一个C主义。
当 C 需要 compare
类型的函数时,它们总是返回一个 int
。 C++ 只是推动了这一点(不幸的是)。
但是,返回 int
实际上可能是最快的方法,因为它通常是正在使用的系统寄存器的大小。 (故意含糊其辞。)
short
和 char
可以施加性能损失,例如 255+7
对 char
和 int
具有不同的值,因此正确的实现不一定简单地将 char
存储在 int
可以去的地方不关心处理它的语义。编译器不一定会优化这带来的低效率。
该方法实际上并不返回集合 { -1, 0, 1 }
中的整数;它实际上可以是 any 整数值。
为什么?我能想到的主要原因是 int
应该是架构的“自然大小”值;对这种大小的值的操作通常至少与对较小或较大值的操作一样快(并且在许多情况下更快)。所以这是一个允许实现足够松弛以使用最快的情况的情况。
如果我要设计一个比较 Foo 类型的两个对象的比较方法,并且我只想返回 -1、0 或 1,那么使用 short 或 char 通常是个好主意吗?
没关系。更好的方法是返回一个布尔值(如果只想比较是否相等)或枚举(更多信息):
enum class MyResult
{
EQUAL,
LESS,
GREATER
};
MyResult AreEqual( const Foo &foo1, const Foo & foo2 )
{
// calculate and return result
}
假设有些人正在将代码从 C 更改为 C++。他们决定将 strcmp
替换为 string::compare
。
由于 strcmp
返回 int
,因此 string::compare
返回 int
作为礼物更容易。
可能是为了让它更像strcmp
,它也有这个set of return values。如果您想移植代码,那么让替换尽可能接近可能会更直观。
此外,返回值不仅仅是 -1
、0
或 1
,而是 <0
、0
或 >0
。
此外,正如所提到的,由于回报受 integral promotion 约束,因此将其缩小是没有意义的。
因为一个布尔返回值只能是两个可能的值(真、假),而比较函数可以返回三个可能的值(小于、等于、大于)。
更新
虽然当然可以返回一个有符号的short,但如果你真的想实现你自己的比较函数,你可以返回一个带有两个布尔值的半字节或结构值。
short
和 char
作为 int
的替代品。
return values are rvalues, subject to integral promotion
进行更多解释,那就太好了。signed char
怎么办?它的行为是否与带符号的char
相同,还是不同的类型?