C++ 为什么 f(i = -1, i = -1) 未定义行为?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21670459/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:46:15  来源:igfitidea点击:

Why is f(i = -1, i = -1) undefined behavior?

c++language-lawyerundefined-behavior

提问by Nicu Stiurca

I was reading about order of evaluation violations, and they give an example that puzzles me.

我正在阅读有关评估顺序违规的内容,他们举了一个让我感到困惑的例子。

1) If a side effect on a scalar object is un-sequenced relative to another side effect on the same scalar object, the behavior is undefined.

// snip
f(i = -1, i = -1); // undefined behavior

1) 如果一个标量对象的副作用相对于同一个标量对象的另一个副作用是未排序的,则行为是未定义的。

// snip
f(i = -1, i = -1); // undefined behavior

In this context, iis a scalar object, which apparently means

在这种情况下,i是一个标量对象,这显然意味着

Arithmetic types (3.9.1), enumeration types, pointer types, pointer to member types (3.9.2), std::nullptr_t, and cv-qualified versions of these types (3.9.3) are collectively called scalar types.

算术类型 (3.9.1)、枚举类型、指针类型、成员类型指针 (3.9.2)、std::nullptr_t 和这些类型的 cv 限定版本 (3.9.3) 统称为标量类型。

I don't see how the statement is ambiguous in that case. It seems to me that regardless of if the first or second argument is evaluated first, iends up as -1, and both arguments are also -1.

在这种情况下,我不明白该声明是如何模棱两可的。在我看来,无论是首先评估第一个参数还是第二个参数,i最终都为-1,并且两个参数也是-1.

Can someone please clarify?

有人可以澄清吗?



UPDATE

更新

I really appreciate all the discussion. So far, I like @harmic's answera lot since it exposes the pitfalls and intricacies of defining this statement in spite of how straight forward it looks at first glance. @acheong87points out some issues that come up when using references, but I think that's orthogonal to the unsequenced side effects aspect of this question.

我非常感谢所有的讨论。到目前为止,我非常喜欢@harmic 的回答,因为它暴露了定义此语句的陷阱和复杂性,尽管乍一看它是多么直截了当。@acheong87指出了使用引用时出现的一些问题,但我认为这与此问题的未排序副作用方面是正交的。



SUMMARY

概括

Since this question got a ton of attention, I will summarize the main points/answers. First, allow me a small digression to point out that "why" can have closely related yet subtly different meanings, namely "for what cause", "for what reason", and "for what purpose". I will group the answers by which of those meanings of "why" they addressed.

由于这个问题引起了大量关注,我将总结要点/答案。首先,请允许我稍微跑题一下,指出“为什么”可以具有密切相关但又略有不同的含义,即“为了什么原因”、“出于什么原因”和“为了什么目的”。我将根据他们解决的“为什么”的含义对答案进行分组。

for what cause

什么原因

The main answer here comes from Paul Draper, with Martin Jcontributing a similar but not as extensive answer. Paul Draper's answer boils down to

这里的主要答案来自Paul DraperMartin J提供了类似但没有那么广泛的答案。保罗德雷珀的回答归结为

It is undefined behavior because it is not defined what the behavior is.

它是未定义的行为,因为它没有定义行为是什么。

The answer is overall very good in terms of explaining what the C++ standard says. It also addresses some related cases of UB such as f(++i, ++i);and f(i=1, i=-1);. In the first of the related cases, it's not clear if the first argument should be i+1and the second i+2or vice versa; in the second, it's not clear if ishould be 1 or -1 after the function call. Both of these cases are UB because they fall under the following rule:

就解释 C++ 标准所说的内容而言,答案总体上非常好。它还解决了 UB 的一些相关案例,例如f(++i, ++i);f(i=1, i=-1);。在第一个相关案例中,不清楚第一个参数是否应该是i+1,第二个参数是否应该是,i+2反之亦然;在第二个中,不清楚i在函数调用后应该是 1 还是 -1。这两种情况都是 UB,因为它们属于以下规则:

If a side effect on a scalar object is unsequenced relative to another side effect on the same scalar object, the behavior is undefined.

如果一个标量对象的副作用相对于同一标量对象的另一个副作用是未排序的,则行为是未定义的。

Therefore, f(i=-1, i=-1)is also UB since it falls under the same rule, despite that the intention of the programmer is (IMHO) obvious and unambiguous.

因此,f(i=-1, i=-1)也是 UB,因为它属于同一规则,尽管程序员的意图(恕我直言)是明显且明确的。

Paul Draper also makes it explicit in his conclusion that

保罗·德雷珀 (Paul Draper) 在他的结论中也明确指出

Could it have been defined behavior? Yes. Was it defined? No.

它可以被定义为行为吗?是的。有没有定义?不。

which brings us to the question of "for what reason/purpose was f(i=-1, i=-1)left as undefined behavior?"

这给我们带来了“出于什么原因/目的被f(i=-1, i=-1)保留为未定义行为?”的问题。

for what reason / purpose

出于什么原因/目的

Although there are some oversights (maybe careless) in the C++ standard, many omissions are well-reasoned and serve a specific purpose. Although I am aware that the purpose is often either "make the compiler-writer's job easier", or "faster code", I was mainly interested to know if there is a good reason leavef(i=-1, i=-1)as UB.

尽管 C++ 标准中存在一些疏忽(可能是粗心大意),但许多疏漏是有充分理由的,并且服务于特定目的。尽管我知道目的通常是“使编译器编写者的工作更轻松”或“更快的代码”,但我主要想知道是否有充分的理由离开f(i=-1, i=-1)UB。

harmicand supercatprovide the main answers that provide a reasonfor the UB. Harmic points out that an optimizing compiler that might break up the ostensibly atomic assignment operations into multiple machine instructions, and that it might further interleave those instructions for optimal speed. This could lead to some very surprising results: iends up as -2 in his scenario! Thus, harmic demonstrates how assigning the same valueto a variable more than once can have ill effects if the operations are unsequenced.

harmicsupercat提供了为 UB提供原因的主要答案。Harmic 指出,优化编译器可能会将表面上的原子赋值操作分解为多个机器指令,并且可能会进一步交错这些指令以获得最佳速度。这可能会导致一些非常令人惊讶的结果:i在他的场景中最终为 -2!因此,harmic 演示了如果操作未按顺序将相同的值多次分配给变量会产生不良影响。

supercat provides a related exposition of the pitfalls of trying to get f(i=-1, i=-1)to do what it looks like it ought to do. He points out that on some architectures, there are hard restrictions against multiple simultaneous writes to the same memory address. A compiler could have a hard time catching this if we were dealing with something less trivial than f(i=-1, i=-1).

supercat 对尝试f(i=-1, i=-1)去做它看起来应该做的事情的陷阱提供了相关的说明。他指出,在某些架构上,对同一内存地址的多个同时写入存在严格限制。如果我们处理的东西比f(i=-1, i=-1).

davidfalso provides an example of interleaving instructions very similar to harmic's.

davidf还提供了一个与harmic 非常相似的交错指令示例。

Although each of harmic's, supercat's and davidf' examples are somewhat contrived, taken together they still serve to provide a tangible reason why f(i=-1, i=-1)should be undefined behavior.

尽管每个harmic、supercat 和davidf 的例子都有些人为,但将它们放在一起仍然可以为为什么f(i=-1, i=-1)应该是未定义的行为提供一个切实的理由。

I accepted harmic's answer because it did the best job of addressing all meanings of why, even though Paul Draper's answer addressed the "for what cause" portion better.

我接受了哈米奇的回答,因为它在解决所有原因的含义方面做得最好,尽管保罗·德雷珀的回答更好地解决了“原因”部分。

other answers

其他答案

JohnBpoints out that if we consider overloaded assignment operators (instead of just plain scalars), then we can run into trouble as well.

JohnB指出,如果我们考虑重载赋值运算符(而不仅仅是简单的标量),那么我们也会遇到麻烦。

回答by harmic

Since the operations are unsequenced, there is nothing to say that the instructions performing the assignment cannot be interleaved. It might be optimal to do so, depending on CPU architecture. The referenced page states this:

由于操作是无序的,因此没有什么可以说执行分配的指令不能交错。这样做可能是最佳选择,具体取决于 CPU 架构。引用的页面说明了这一点:

If A is not sequenced before B and B is not sequenced before A, then two possibilities exist:

  • evaluations of A and B are unsequenced: they may be performed in any order and may overlap (within a single thread of execution, the compiler may interleave the CPU instructions that comprise A and B)

  • evaluations of A and B are indeterminately-sequenced: they may be performed in any order but may not overlap: either A will be complete before B, or B will be complete before A. The order may be the opposite the next time the same expression is evaluated.

如果 A 未在 B 之前排序且 B 未在 A 之前排序,则存在两种可能性:

  • A 和 B 的计算是无序的:它们可以按任何顺序执行并且可能重叠(在单个执行线程中,编译器可能会交错包含 A 和 B 的 CPU 指令)

  • A 和 B 的计算是不确定顺序的:它们可以按任何顺序执行,但不能重叠:A 将在 B 之前完成,或者 B 将在 A 之前完成。下一次相同的表达式时,顺序可能相反被评估。

That by itself doesn't seem like it would cause a problem - assuming that the operation being performed is storing the value -1 into a memory location. But there is also nothing to say that the compiler cannot optimize that into a separate set of instructions that has the same effect, but which could fail if the operation was interleaved with another operation on the same memory location.

这本身似乎不会导致问题 - 假设正在执行的操作将值 -1 存储到内存位置。但是也没有什么可以说编译器无法将其优化为具有相同效果的单独指令集,但如果该操作与同一内存位置上的另一个操作交错,则可能会失败。

For example, imagine that it was more efficient to zero the memory, then decrement it, compared with loading the value -1 in. Then this:

例如,想象一下,与加载值 -1 相比,将内存归零然后递减它更有效。 那么这个:

f(i=-1, i=-1)

might become:

可能会变成:

clear i
clear i
decr i
decr i

Now i is -2.

现在我是-2。

It is probably a bogus example, but it is possible.

这可能是一个虚假的例子,但它是可能的。

回答by Paul Draper

First, "scalar object" means a type like a int, float, or a pointer (see What is a scalar Object in C++?).

首先,“标量对象”是指像 a intfloat或指针这样的类型(请参阅什么是 C++ 中的标量对象?)。



Second, it may seem more obvious that

其次,似乎更明显的是

f(++i, ++i);

would have undefined behavior. But

会有未定义的行为。但

f(i = -1, i = -1);

is less obvious.

不太明显。

A slightly different example:

一个稍微不同的例子:

int i;
f(i = 1, i = -1);
std::cout << i << "\n";

What assignment happened "last", i = 1, or i = -1? It's not defined in the standard. Really, that means icould be 5(see harmic's answer for a completely plausible explanation for how this chould be the case). Or you program could segfault. Or reformat your hard drive.

“最后”发生了什么分配,i = 1, 或i = -1?它在标准中没有定义。真的,这意味着i可能是5(有关情况如何的完全合理的解释,请参阅harmic的回答)。或者你的程序可能会出现段错误。或者重新格式化你的硬盘。

But now you ask: "What about my example? I used the same value (-1) for both assignments. What could possibly be unclear about that?"

但是现在你问:“我的例子怎么样?我在-1两个作业中使用了相同的值 ( )。这可能有什么不清楚的地方?

You are correct...except in the way the C++ standards committee described this.

你是对的……除了 C++ 标准委员会描述这一点的方式。

If a side effect on a scalar object is unsequenced relative to another side effect on the same scalar object, the behavior is undefined.

如果一个标量对象的副作用相对于同一标量对象的另一个副作用是未排序的,则行为是未定义的。

They couldhave made a special exception for your special case, but they didn't. (And why should they? What use would that ever possibly have?) So, icould still be 5. Or your hard drive could be empty. Thus the answer to your question is:

他们本可以为你的特殊情况做一个特殊的例外,但他们没有。(他们为什么要这样做?那可能有什么用?)所以,i仍然可能是5. 或者您的硬盘驱动器可能是空的。因此,您的问题的答案是:

It is undefined behavior because it is not defined what the behavior is.

它是未定义的行为,因为它没有定义行为是什么。

(This deserves emphasis because many programmers think "undefined" means "random", or "unpredictable". It doesn't; it means not defined by the standard. The behavior could be 100% consistent, and still be undefined.)

(这值得强调,因为许多程序员认为“未定义”意味着“随机”或“不可预测”。事实并非如此;这意味着未由标准定义。行为可能是 100% 一致的,但仍然是未定义的。)

Could it have been defined behavior? Yes. Was it defined? No. Hence, it is "undefined".

它可以被定义为行为吗?是的。有没有定义?不。因此,它是“未定义的”。

That said, "undefined" doesn't mean that a compiler will format your hard drive...it means that it couldand it would still be a standards-compliant compiler. Realistically, I'm sure g++, Clang, and MSVC will all do what you expected. They just wouldn't "have to".

也就是说,“未定义”并不意味着编译器会格式化您的硬盘……这意味着它可以并且仍然是符合标准的编译器。实际上,我确信 g++、Clang 和 MSVC 都能满足您的预期。他们只是不会“不得不”。



A different question might be Why did the C++ standards committee choose to make this side-effect unsequenced?. That answer will involve history and opinions of the committee. Or What is good about having this side-effect unsequenced in C++?, which permits any justification, whether or not it was the actual reasoning of the standards committee. You could ask those questions here, or at programmers.stackexchange.com.

一个不同的问题可能是为什么 C++ 标准委员会选择使这种副作用无序?. 这个答案将涉及委员会的历史和意见。或者在 C++ 中不排序这种副作用有什么好处?,这允许任何理由,无论它是否是标准委员会的实际推理。您可以在此处或在programmers.stackexchange.com 上提出这些问题。

回答by Ingo

A practical reason to not make an exception from the rules just because the two values are the same:

不因为两个值相同而从规则中例外的一个实际原因:

// config.h
#define VALUEA  1

// defaults.h
#define VALUEB  1

// prog.cpp
f(i = VALUEA, i = VALUEB);

Consider the case this was allowed.

考虑允许这样做的情况。

Now, some months later, the need arises to change

现在,几个月后,需要改变

 #define VALUEB 2

Seemingly harmless, isn't it? And yet suddenly prog.cpp wouldn't compile anymore. Yet, we feel that compilation should not depend on the value of a literal.

看起来无害,不是吗?然而突然 prog.cpp 不再编译了。然而,我们认为编译不应该依赖于文字的值。

Bottom line: there is no exception to the rule because it would make successful compilation depend on the value (rather the type) of a constant.

底线:规则没有例外,因为它会使成功编译取决于常量的值(而不是类型)。

EDIT

编辑

@HeartWare pointed outthat constant expressions of the form A DIV Bare not allowed in some languages, when Bis 0, and cause compilation to fail. Hence changing of a constant could cause compilation errors in some other place. Which is, IMHO, unfortunate. But it is certainly good to restrict such things to the unavoidable.

@HeartWare 指出A DIV B在某些语言中不允许使用表单的常量表达式,whenB为 0,导致编译失败。因此,常量的更改可能会在其他地方导致编译错误。恕我直言,不幸的是。但将这些事情限制在不可避免的事情上当然是好的。

回答by davidf

The confusion is that storing a constant value into a local variable is not one atomic instruction on every architecture the C is designed to be run on. The processor the code runs on matters more than the compiler in this case. For example, on ARM where each instruction can not carry a complete 32 bits constant, storing an int in a variable needs more that one instruction. Example with this pseudo code where you can only store 8 bits at a time and must work in a 32 bits register, i is a int32:

令人困惑的是,将常量值存储到局部变量中并不是 C 旨在运行的每个体系结构上的一条原子指令。在这种情况下,运行代码的处理器比编译器更重要。例如,在每条指令不能携带完整 32 位常量的 ARM 上,在变量中存储一个 int 需要多于一条指令。使用此伪代码的示例,您一次只能存储 8 位并且必须在 32 位寄存器中工作,i 是一个 int32:

reg = 0xFF; // first instruction
reg |= 0xFF00; // second
reg |= 0xFF0000; // third
reg |= 0xFF000000; // fourth
i = reg; // last

You can imagine that if the compiler wants to optimize it may interleave the same sequence twice, and you don't know what value will get written to i; and let's say that he is not very smart:

你可以想象,如果编译器想要优化它可能会在同一个序列中交错两次,而你不知道什么值会被写入到 i 中;假设他不是很聪明:

reg = 0xFF;
reg |= 0xFF00;
reg |= 0xFF0000;
reg = 0xFF;
reg |= 0xFF000000;
i = reg; // writes 0xFF0000FF == -16776961
reg |= 0xFF00;
reg |= 0xFF0000;
reg |= 0xFF000000;
i = reg; // writes 0xFFFFFFFF == -1

However in my tests gcc is kind enough to recognize that the same value is used twice and generates it once and does nothing weird. I get -1, -1 But my example is still valid as it is important to consider that even a constant may not be as obvious as it seems to be.

然而,在我的测试中,gcc 很友好地识别出相同的值被使用两次并生成一次并且没有任何奇怪的事情。我得到 -1, -1 但我的例子仍然有效,因为重要的是要考虑到即使是一个常数也可能不像看起来那么明显。

回答by supercat

Behavior is commonly specified as undefined if there is some conceivable reason why a compiler which was trying to be "helpful" might do something which would cause totally unexpected behavior.

如果有一些可以想象的原因,为什么试图“有用”的编译器可能会做一些会导致完全意外行为的事情,则行为通常被指定为未定义。

In the case where a variable is written multiple times with nothing to ensure that the writes happen at distinct times, some kinds of hardware might allow multiple "store" operations to be performed simultaneously to different addresses using a dual-port memory. However, some dual-port memories expressly forbid the scenario where two stores hit the same address simultaneously, regardless of whether or not the values written match. If a compiler for such a machine notices two unsequenced attempts to write the same variable, it might either refuse to compile or ensure that the two writes cannot get scheduled simultaneously. But if one or both of the accesses is via a pointer or reference, the compiler might not always be able to tell whether both writes might hit the same storage location. In that case, it might schedule the writes simultaneously, causing a hardware trap on the access attempt.

在变量被多次写入但没有任何内容以确保写入发生在不同时间的情况下,某些类型的硬件可能允许使用双端口存储器同时对不同地址执行多个“存储”操作。但是,一些双端口存储器明确禁止两个存储同时命中同一地址的情况,无论写入的值是否匹配. 如果此类机器的编译器注意到两次未按顺序写入同一变量的尝试,它可能会拒绝编译或确保无法同时安排两次写入。但是,如果其中一个或两个访问是通过指针或引用进行的,编译器可能无法始终判断两个写入是否会访问相同的存储位置。在这种情况下,它可能会同时安排写入,从而导致访问尝试中的硬件陷阱。

Of course, the fact that someone might implement a C compiler on such a platform does not suggest that such behavior shouldn't be defined on hardware platforms when using stores of types small enough to be processed atomically. Trying to store two different values in unsequenced fashion could cause weirdness if a compiler isn't aware of it; for example, given:

当然,有人可能在这样的平台上实现 C 编译器这一事实并不意味着在使用足够小以进行原子处理的类型存储时,不应在硬件平台上定义这种行为。如果编译器不知道,尝试以未排序的方式存储两个不同的值可能会导致奇怪;例如,给定:

uint8_t v;  // Global

void hey(uint8_t *p)
{
  moo(v=5, (*p)=6);
  zoo(v);
  zoo(v);
}

if the compiler in-lines the call to "moo" and can tell it doesn't modify "v", it might store a 5 to v, then store a 6 to *p, then pass 5 to "zoo", and then pass the contents of v to "zoo". If "zoo" doesn't modify "v", there should be no way the two calls should be passed different values, but that could easily happen anyway. On the other hand, in cases where both stores would write the same value, such weirdness could not occur and there would on most platforms be no sensible reason for an implementation to do anything weird. Unfortunately, some compiler writers don't need any excuse for silly behaviors beyond "because the Standard allows it", so even those cases aren't safe.

如果编译器内联对“moo”的调用并且可以告诉它不修改“v”,它可能会存储一个 5 到 v,然后存储一个 6 到 *p,然后将 5 传递给“zoo”,然后将 v 的内容传递给“zoo”。如果“zoo”不修改“v”,则这两个调用应该无法传递不同的值,但无论如何这很容易发生。另一方面,在两个存储都写入相同值的情况下,这种奇怪的情况不会发生,并且在大多数平台上,实现做任何奇怪的事情都没有合理的理由。不幸的是,一些编译器作者不需要任何借口来解释“因为标准允许”之外的愚蠢行为,所以即使是这些情况也不安全。

回答by Amadan

The fact that the result would be the same in most implementations in thiscase is incidental; the order of evaluation is still undefined. Consider f(i = -1, i = -2): here, order matters. The only reason it doesn't matter in your example is the accident that both values are -1.

这种情况下,大多数实现中的结果是相同的,这一事实是偶然的。评估的顺序仍然未定义。考虑f(i = -1, i = -2):在这里,顺序很重要。在您的示例中无关紧要的唯一原因是两个值都是-1.

Given that the expression is specified as one with an undefined behaviour, a maliciously compliant compiler might display an inappropriate image when you evaluate f(i = -1, i = -1)and abort the execution - and still be considered completely correct. Luckily, no compilers I am aware of do so.

鉴于表达式被指定为具有未定义行为的表达式,恶意兼容编译器可能会在您评估f(i = -1, i = -1)和中止执行时显示不适当的图像- 并且仍然被认为是完全正确的。幸运的是,我所知道的编译器都没有这样做。

回答by Martin J.

It looks to me like the only rule pertaining to sequencing of function argument expression is here:

在我看来,与函数参数表达式排序有关的唯一规则是:

3) When calling a function (whether or not the function is inline, and whether or not explicit function call syntax is used), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.

3) 调用函数时(无论函数是否内联,以及是否使用显式函数调用语法),与任何参数表达式或指定被调用函数的后缀表达式相关的每个值计算和副作用都是在被调用函数体中的每个表达式或语句执行之前进行排序。

This does not define sequencing between argument expressions, so we end up in this case:

这并没有定义参数表达式之间的顺序,所以我们在这种情况下结束:

1) If a side effect on a scalar object is unsequenced relative to another side effect on the same scalar object, the behavior is undefined.

1) 如果一个标量对象的副作用相对于同一个标量对象的另一个副作用是无序的,则行为是未定义的。

In practice, on most compilers, the example you quoted will run fine (as opposed to "erasing your hard disk" and other theoretical undefined behavior consequences).
It is, however, a liability, as it depends on specific compiler behaviour, even if the two assigned values are the same. Also, obviously, if you tried to assign different values, the results would be "truly" undefined:

实际上,在大多数编译器上,您引用的示例将运行良好(与“擦除硬盘”和其他理论上的未定义行为后果相反)。
然而,这是一种负担,因为它取决于特定的编译器行为,即使两个分配的值相同。此外,显然,如果您尝试分配不同的值,结果将是“真正”未定义的:

void f(int l, int r) {
    return l < -1;
}
auto b = f(i = -1, i = -2);
if (b) {
    formatDisk();
}

回答by AlexD

C++17defines stricter evaluation rules. In particular, it sequences function arguments (although in unspecified order).

C++17定义了更严格的评估规则。特别是,它对函数参数进行排序(尽管以未指定的顺序)。

N5659 §4.6:15
Evaluations Aand Bare indeterminately sequenced when either Ais sequenced before Bor Bis sequenced before A, but it is unspecified which. [ Note: Indeterminately sequenced evaluations cannot overlap, but either could be executed first. —end note]

N5659 § 8.2.2:5
The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.

N5659 §4.6:15
AB之前排序或BA之前排序时, 评估AB是不确定,但未指定哪个。[注意:不确定排序的评估不能重叠,但可以先执行。—尾注]

N5659 § 8.2.2:5
参数的初始化,包括每个相关的值计算和副作用,相对于任何其他参数的初始化顺序是不确定的。

It allows some cases which would be UB before:

它允许一些以前是 UB 的情况:

f(i = -1, i = -1); // value of i is -1
f(i = -1, i = -2); // value of i is either -1 or -2, but not specified which one

回答by JohnB

The assignment operator could be overloaded, in which case the order could matter:

赋值运算符可能会被重载,在这种情况下,顺序可能很重要:

struct A {
    bool first;
    A () : first (false) {
    }
    const A & operator = (int i) {
        first = !first;
        return * this;
    }
};

void f (A a1, A a2) {
    // ...
}


// ...
A i;
f (i = -1, i = -1);   // the argument evaluated first has ax.first == true

回答by Peng Zhang

This is just answering the "I'm not sure what "scalar object" could mean besides something like an int or a float".

这只是回答“除了 int 或 float 之类的东西之外,我不确定“标量对象”可能意味着什么”。

I would interpret the "scalar object" as a abbreviation of "scalar type object", or just "scalar type variable". Then, pointer, enum(constant) are of scalar type.

我会将“标量对象”解释为“标量类型对象”的缩写,或者只是“标量类型变量”。然后,pointerenum(常数)是标量类型的。

This is a MSDN article of Scalar Types.

这是Scalar Types的 MSDN 文章。