MSVC 将仅移动结构参数解释为指针

MSVC interpreting move-only struct argument as a pointer

我有一个简单的单成员结构,其中包含已删除的副本 construction/assignment 和默认移动 construction/assignment。我试图通过值和 return 成员将这些结构之一传递给函数 - 非常简单。

struct NoCopy {
    explicit NoCopy(int x) : x{x} {}

    NoCopy(const NoCopy&) = delete;
    NoCopy& operator=(const NoCopy&) = delete;

    NoCopy(NoCopy&&) = default;
    NoCopy& operator=(NoCopy&&) = default;

    int x;
};

// noinline to ensure the crash is reproducible in release
// not required to reproduce the problem code
__declspec(noinline) int problem_function(NoCopy x) {
    return x.x;
}

int main() {
    return problem_function(NoCopy{ 1 });
}

问题是当使用 MSVC 编译时,这个函数崩溃了。

查看反汇编,似乎在删除复制构造函数时,MSVC 尝试将 x 解释为 NoCopy*,随后的成员读取会导致分段错误。

这里有一个godbolt的例子,gcc和clang供参考:https://godbolt.org/z/jG7kIw

请注意,gcc 和 clang 的行为均符合预期。 另请注意,这在优化和未优化的版本中都会发生,并且似乎会影响 MSVC 2015 和 2017。

作为参考,我正在使用 Visual Studio Professional 2015(14.0.25431.01 更新 3)在我的机器上进行编译 - 我主要测试 x64 版本。我的崩溃重现平台工具集设置为 v140。

所以我的问题是:对此有任何合理的解释,还是我在查看编译器错误。

编辑:我已提交错误报告 over here

编辑 #2:如果像我一样,您遇到了类似的问题并且无法轻松更新 VS - 似乎手动定义移动 constructor/assignment 运算符 而不是使用 = default 导致 MSVC 在调用站点吐出正确的代码并避免崩溃。 here's a new godbolt

因此,std::unique_ptr 之类的内容似乎没有受到影响。结构大小似乎也是一个因素。

除了严重的编译器错误,我看不出这是什么。代码有效。

在两个 MSVS 版本中已经破坏了如此基础的东西,这似乎确实很奇怪,但如果我不得不猜测这可能是由于相对较新的 C++17 复制省略支持。 (当然,在这种情况下,我使用的术语 "support" 有点松散。)

(网上提出的OP的VS bug here.)