【问题标题】:Does the following code invoke undefined behaviour?以下代码是否调用未定义的行为?
【发布时间】:2015-02-16 08:23:52
【问题描述】:

我想做这样的事情

#include <iostream>
#include <memory>

struct Foo {};

using FooPtr = std::unique_ptr<Foo>;

FooPtr makeFoo() { return FooPtr(new Foo()); }

struct Baz
{
    Baz(FooPtr foo) : Baz(std::move(foo), bar(foo)) {}
    Baz(FooPtr foo, int something) : _foo{ std::move(foo) }, _something{ something } {}
private:
    FooPtr _foo;
    int _something;

    static int bar(const FooPtr & ptr)
    {
        std::cout << "bar!" << std::endl;
        return 42;
    }
};

int main() {
    Baz baz(makeFoo());
    return 0;
}

我的问题是:函数参数评估的顺序是未指定的,因此传递一个将从一个参数中移动的值以及使用 相同实例调用另一个函数的结果是否安全>,作为对 const 的引用传递,作为另一个参数?

我认为问题可以归结为何时执行实际的移动操作,这一点我并不完全清楚(尤其是在开启优化时)。

【问题讨论】:

    标签: c++ c++11 move rvalue sequence-points


    【解决方案1】:

    在执行std::unique_ptr&lt;Foo&gt; 的移动构造函数之前,不会发生实际的“移动”(所有std::move() 所做的都是将const FooPtr &amp; 右值转换为FooPtr &amp;&amp; 右值引用)。在调用您委派给的两个参数 Baz 构造函数之前,不会发生这种情况。为了发生这种情况,必须首先评估该构造函数的所有参数。因此,在评估这些参数时对foo 对象的任何使用都将发生在unique_ptr 实例的实际“移动”之前。

    由于您通过值传递FooPtr(又名std::unique_ptr&lt;Foo&gt;,并且std::unique_ptr 是仅移动的,因此在评估双参数构造函数的第一个参数时将触发移动构造。由于参数的评估未指定,该移动可能会或可能不会在评估第二个参数之前发生。因此,您的示例的行为未指定

    【讨论】:

    • 我认为这是不正确的,因为在调用构造函数之前,您必须构造按值传递的参数。为第二个构造函数构造FooPtr foo 将使foo“无效”。
    • @RomanL:是的,我认为你是对的(也请参阅 galop1n 对另一个答案的评论)。
    • 证明,反转两个参数会改变结果:coliru.stacked-crooked.com/a/37f85e8a5397eec2(因为 clang 最常从左到右计算,但这取决于编译器)
    • 需要明确的是,对bar的调用是否看到一个为空的参数,并且没有UB。
    • @MattMcNabb:我不太擅长语言律师,但有区别吗?我假设有一些严格的 UB 定义没有涵盖这一点,而我认为直觉上它会被视为这样,因为它未指定参数是否为空。
    【解决方案2】:

    std::move 不是操作,它是对 r 值引用的强制转换。移动操作将发生在另一个 Baz 构造函数中。因此,您正在做的事情应该会奏效。

    【讨论】:

    • 如果没有复制省略,第一个参数 Baz 构造函数执行移动构造,这可能发生在第二个参数调用 bar 之前或之后。所以结果是未指定的
    【解决方案3】:

    Scott Meyers 在他的帖子 Should move-only types ever be passed by value? 中提到了 similar SO question。似乎没有明确的答案是否应该按值传递,但这样做显然会导致未指定的行为,在您的情况下也是如此。

    【讨论】:

      猜你喜欢
      • 2015-05-26
      • 1970-01-01
      • 1970-01-01
      • 2021-02-14
      • 1970-01-01
      • 2017-11-04
      • 2011-07-25
      • 2013-08-25
      • 1970-01-01
      相关资源
      最近更新 更多