提问人:lcmylin 提问时间:9/14/2017 最后编辑:Communitylcmylin 更新时间:9/14/2017 访问量:226
访问作为“inout”传递的“var”是未定义的行为?
Accessing a `var` passed as `inout` is undefined behavior?
问:
从 inout
参数的文档中:
输入输出参数传递如下:
- 调用函数时,将复制参数的值。
- 在函数的主体中,副本被修改。
- 当函数返回时,副本的值将分配给原始参数。
仅从这个描述中,我假设在作为参数传递的范围内修改作为参数传递的变量是没有意义的,因为原始变量保证在调用返回后被覆盖。举个人为的例子:inout
inout
var x: Int = 5
({ (inoutX: inout Int) in
inoutX = 7
x = 6
})(&x)
print(x) // Expecting "7"
原始变量可通过变异捕获进行访问,因此仍可分配给该变量。预期的打印输出为“7”,因为这是函数调用结束时的值。但是如果我在 Swift 4 REPL 中运行它,我实际上会得到“6”!x
inoutX
该文档对此行为进行了一些说明:
作为优化,当参数是存储在内存中物理地址的值时,函数体内部和外部都使用相同的内存位置。
但随后又有一句非常明显不准确的声明:
优化的行为称为引用调用;它满足了复制-传入复制模型的所有要求,同时消除了复制的开销。
显然,优化后的行为不符合参数声称符合的按值结果调用约定。然后,文档承认了这一点,但反过来,解释了为什么您不应该依赖引用调用行为:inout
使用复制-传入-复制输出给出的模型编写代码,而不依赖于按引用调用优化,以便无论是否进行优化,它都能正常运行。
不要访问作为 in-out 参数传递的值,即使原始参数在当前作用域中可用。当函数返回时,您对原始内容所做的更改将被副本的值覆盖。不要依赖按引用调用优化的实现来尝试防止更改被覆盖。
那么我可以收集到的是,参数是按值结果调用的,除非它们是按引用调用的。而且,根据文档不希望您依赖引用调用语义的程度,我只能猜测优化不是在一组定义明确的情况下执行的。如果是这样的话,那么我只能得出结论,在输入范围内访问作为 inout
参数传递的变量是未定义的行为。inout
这是一个相对不幸的结论,我对文档的不情愿感到困惑。为什么它会试图将参数表示为遵循特定的调用约定,而(除了 setter 或属性观察者之外)这些调用约定的语义无法以定义的方式观察到?令人困惑的是,我在这里怀疑自己的结论,因此问题来了:我的理解正确吗?inout
答: 暂无答案
评论
Simultaneous accesses to 0x1005e3ec0, but modification requires exclusive access.
inout
set
绑定到的原始变量”并且......inout
参数传递,或者可以向某个方法传递一个回调,该回调以某种方式访问调用该方法的同一变量。这样做大多是不鼓励的,但这不是被禁止的,编译器和标准库都必须向后弯腰,以确保程序在发生这种情况时不会表现得太糟糕。....inout