【问题标题】:Why does 'auto' not respect the unary minus operator?为什么'auto'不尊重一元减号运算符?
【发布时间】:2019-01-01 23:36:17
【问题描述】:

我对 C++ 很陌生,但我发现 auto 的这种行为很奇怪:

class A{};

int main() {
    A a;
    auto x = -(sizeof(a));
    cout << x << endl;
    return 0;
}

在这种情况下,变量 x 是 unsigned,尽管我在变量初始化时使用了一元减号运算符。为什么只考虑了sizeof(std::size_t)的返回类型,却没有考虑到因为使用了操作符而存储的数字会是负数?

我知道size_t 是一个无符号整数。

我已经在 GCC 8.1.0 和 C++17 中尝试过。

【问题讨论】:

  • 为什么你会期待不同的东西?当您执行cout &lt;&lt; -(sizeof(a)) &lt;&lt; endl; 时,也会发生同样的事情。这与汽车无关。
  • 因为 C 设计者希望 - 在无符号值上有效。就我个人而言,我会使其无效。
  • 这可能是对 auto 的误解,它没有为 C++ 语言添加任何新内容,它使用了与模板参数类型推导语言中已经存在的机制完全相同的机制
  • @curiousguy 除了一元减号。通过说0u - x,可以得到和-x一样的效果。也许我什至会禁止在运算符表达式中混合有符号/无符号。 IMO 仍然允许 -1u.. 可能很有用,所以我可以添加一条规则,即无符号的一元减号仅允许在无符号的文字上使用,这类似于禁止在运算符表达式中混合符号。
  • @curiousguy 是的,因为环绕只是减法的一种特殊情况。只有当右边大于左边时,你才有环绕。另一方面,对于否定,环绕是大多数情况,只有0 是例外。

标签: c++ sizeof unsigned


【解决方案1】:

这里的实际问题是一元减号运算符的使用,就像其他内置算术运算符一样,是整体提升的主题。令人惊讶的是,对size_t 应用一元减号的结果仍然是size_t,没有必要责怪auto。

反例。在这种情况下,由于x 的整体促销类型将为int,因此输出将为-1:

unsigned short a{1};
auto x{-a};
cout << x << endl;

【讨论】:

  • @acraig5075 不,我的意思是令人惊讶。为什么一元减号被允许首先应用于无符号类型是相当令人困惑的。我的意思是一元减号应该改变值的符号,但无符号值不能改变符号。
  • VTT 二进制减号适用于无符号值,那么为什么一元减号不应该呢?
  • 积分推广和它有什么关系?据我所知,这里没有任何宣传; size_t 进,size_t 出...?!?
  • @VTT 您可以将无符号整数视为正整数或以 2^n 为模的整数。无符号无符号的否定仅在模算术 (Z/(2^n)Z) 方面才有意义。但是说字节数是一个整数模是荒谬的,它是一个正数。
  • @curiousguy:基本问题是无符号类型用于两个不相交的目的:处理模算术,或保存上限超过相应有符号类型的上限的自然数。超过两个。不幸的是,C 并没有允许程序员指定需要哪个,而是简单地假设“小型”无符号类型应该具有后一种行为,而“全尺寸”类型应该具有前者。
【解决方案2】:

您的表达式-(sizeof(a)) 将一元- 运算符应用于无符号类型的值。一元 - 运算符不会将无符号整数值转换为有符号整数值;它更确切地说定义了哪个无符号值将是这样一个操作的结果,如下所示(参见unary arithmetic operators at cppreference.com):

内置的一元减号运算符计算其负数 提升的操作数。对于无符号 a,-a 的值为 2^b -a,其中b是提升后的位数。

因此,即使可能令人惊讶,auto 也能正常工作,因为将一元 - 运算符应用于无符号值仍然是无符号值。

【讨论】:

  • "运算符不会将无符号整数值转换为有符号整数" 如果您将 unsigned 用于模算术,这完全有道理,而如果您使用它则毫无意义无符号自然数
  • 你可能要提一下size_t和所有的整型一样都需要整型提升,所以如果它小于int,就会在一元减号得到之前提升到int .但我不知道有任何这种不正当的实现。
【解决方案3】:

(一元)- 应用于无符号值的结果是无符号的,sizeof 返回一个无符号值。

一元 - 运算符的操作数应具有算术或无作用域 枚举类型,结果是其操作数的否定。 对整数或枚举操作数执行整数提升。 无符号量的负数是通过减去它的 来自 2^n 的值,其中 n 是提升的操作数中的位数。 结果的类型是提升操作数的类型。

[expr.unary.op]

sizeof 和 sizeof... 的结果是一个类型的常量 std​::​size_­t

[expr.sizeof]

为避免实现定义的行为,您必须在应用 - 之前转换为 int

如果目标类型是有符号的,如果可以,则值不变 以目的地类型表示;否则,值为 实现定义。

[conv.integral]

class A{};

int main() {
    A a;
    auto x = -(int{sizeof(a)});
    cout << x << endl;
    return 0;
}

【讨论】:

  • 感谢您展示auto x = 0-(sizeof(a)); 方式。
  • 如果size_t 的范围大于int(几乎总是如此),则给定size_t x,y=...;,然后x=-y; 将设置x 为一个可能的size_t 值这将使x+y 产量为0。
【解决方案4】:

如果我们看一下:https://en.cppreference.com/w/cpp/language/sizeof,结果是size_t 类型,即unsigned。您明确需要将其声明为 signed int 以允许负值。

你可以写成int,而不是auto,它允许负值。

【讨论】:

  • 所以我必须明确表示它是signed int 吗? auto 是否总是只使用返回类型而不检查它是否有意义?如果是这样,我在此处看到了投射问题。
  • 没错,你需要声明c++你要存储一个负值。 auto 不知道这一点,因为值为 size_t。投射不会给您带来问题,因为您告诉 c++ 它将是 signed int。
  • @CodeShark 这里什么“没有意义”?
猜你喜欢
  • 1970-01-01
  • 2011-07-23
  • 2016-08-24
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 2013-07-30
  • 2011-12-23
相关资源
最近更新 更多