3

参数的文档中inout

传入传出参数如下:

  1. 调用函数时,会复制参数的值。
  2. 在函数体中,副本被修改。
  3. 当函数返回时,副本的值被分配给原始参数。

仅从这个描述中,我会假设在作为inout参数传递的范围内对作为参数传递的变量进行修改inout是没有意义的,因为在调用返回后保证会覆盖原始变量。作为一个人为的例子:

var x: Int = 5

({ (inoutX: inout Int) in
    inoutX = 7
    x = 6
})(&x)

print(x) // Expecting "7"

原始变量x可以通过变异捕获访问,因此仍然可以分配给它。预期的打印输出是“7”,因为这是inoutX函数调用结束时的值。但是如果我在 Swift 4 REPL 中运行它,我实际上会得到“6”!

该文档阐明了这种行为:

作为优化,当参数是存储在内存中物理地址的值时,函数体内部和外部都使用相同的内存位置。

但接下来是一个非常明显不准确的陈述:

优化的行为称为引用调用;它满足了copy-in-copy-out模型的所有要求,同时消除了复制的开销。

显然,优化的行为不满足inout参数声称符合的按值调用结果约定。然后文档承认了这一点,但反过来解释了为什么不应该依赖引用调用行为:

使用由copy-in-copy-out给出的模型编写代码,而不依赖于引用调用优化,以便无论是否有优化它都能正确运行。

不要访问作为输入输出参数传递的值,即使原始参数在当前范围内可用。当函数返回时,您对原始文件的更改将被副本的值覆盖。不要依赖引用调用优化的实现来尝试防止更改被覆盖。

那么我可以收集的是,inout参数是按值调用的结果,除非它们是按引用调用的。而且,基于文档不希望您依赖引用调用语义的程度,我只能猜测优化不是在定义明确的情况下执行的。如果是这种情况,那么我只能得出结论,在它所在的范围内访问作为参数传递的变量是未定义的行为。inoutinout

这是一个相对不幸的结论,我对文档是多么不愿意做出它感到困惑。inout当(除了设置器或属性观察器)这些调用约定的语义不能以定义的方式观察时,为什么它会尝试将参数呈现为遵守特定的调用约定?令人困惑的是,我在这里怀疑自己的结论,所以问题来了:我的理解正确吗?

4

0 回答 0