以下代码是否调用未定义的行为?

时间:2014-12-17 17:35:24

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

我想做这样的事情

#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,作为另一个参数?

我认为这个问题可以归结为实际移动操作的执行时间,我不完全清楚(特别是在开启优化时)。

3 个答案:

答案 0 :(得分:9)

实际&#34;移动&#34;在执行std::unique_ptr<Foo>的移动构造函数之前不会发生(所有std::move()都会将const FooPtr & rvalue转换为FooPtr &&右值引用。在您调用的双参数Baz构造函数被调用之前,这种情况不会发生。为了使其发生,必须首先评估该构造函数的所有参数。因此,foo对象在评估这些参数时的任何使用都将在实际的&#34;运动之前发生。 unique_ptr个实例。

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

答案 1 :(得分:4)

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

答案 2 :(得分:4)

Scott Meyers在帖子similar SO question中提到Should move-only types ever be passed by value?。似乎没有确定的答案是否应该通过价值传递,但这样做显然会导致一种不明确的行为,在你的情况下它也会这样做。