【问题标题】:How to use variable and move variable at the same time?如何同时使用变量和移动变量?
【发布时间】:2020-06-27 00:47:53
【问题描述】:

假设我们有以下代码:

struct some_class : parent
{

    some_class(::other_class oth) :
       parent(some_function(oth.some_property), std::move(oth))
    {}

};

当然构造会导致未定义的行为(在我的例子中是崩溃),因为 c++ 没有指定执行顺序。但是,我怎样才能在运动之前取回财产呢?我无法更改父级。

【问题讨论】:

  • 这里没有未定义的行为。 std::move 只是对右值引用的强制转换。罪魁祸首是parent的构造函数中的代码。由于没有显示,因此很难知道什么可能有效。
  • 没错,不是真的未定义,但绝对出乎意料。根据您使用的编译器,some_function 会在“原始”oth 或“移出”oth 上调用。如果oth.some_property 是例如std::string,则它要么包含其原始值,要么为空。
  • some_function 用它的参数做什么? std::move 实际上并没有自己做任何事情。
  • @Darhuuk 只有parent 的构造函数采用第二个参数按值而不是按引用时才会出现这种情况。否则在parent的构造函数主体执行之前不会有任何问题发生。
  • @walnut 没错,我假设是这样。否则这个问题就没有意义了。

标签: c++ constructor c++17 move order-of-execution


【解决方案1】:

你可以试试委托构造函数:

struct some_class : parent
{

    some_class(::other_class oth) :
       some_class(some_function(oth.some_property), std::move(oth))
    {}

private:
    some_class(const ::Foo& foo, ::other_class&& oth) :
       parent(foo, std::move(oth))
    {}
};

【讨论】:

  • 嗯。如果我将您的答案与 Darhuuk 的答案合并,我将获得 some_class(const ::Foo& foo, ::other_class&& oth) 的委托构造函数,这实际上可能是我寻求的答案?
  • 如果直接调用第二个构造函数,我会发现这会导致非常意外的行为。我会将其设为私有构造函数。
  • 我以为你不能修改parent
  • @Dekakaruk 您建议的编辑不会编译,委托构造函数采用引用而不是右值引用是有原因的
  • @AlanBirtles 我的错,已修复。
【解决方案2】:

正如您所注意到的,问题是由于未指定的执行顺序造成的。 您可以通过使parent 通过右值引用而不是左值获取对象来摆脱未定义的行为。这样它就可以引用现有对象,并且该对象在内存中的数据实际上不会被移动。即:

struct parent {
   parent (int thing, SomeProperty && some_property) { /* Do stuff. */ }
};

在这种情况下,std::move 何时在 oth 上执行并不重要。它的数据实际上不会被移动到另一个对象中,因为parent 需要一个右值引用。因此,即使首先调用 std::move,您的 some_function(我假设它通过 const 左值引用获取 oth)也将有一个合适的对象可以使用。

这里的主要缺点是 parent 现在总是需要右值。所以你不能在不移动它的情况下将它传递给它。这意味着,如果您有一个不想摆脱的对象,您首先必须明确地复制它。即:

other_class wantToKeepThis;
auto foo = parent(wantToKeepThis); // Doesn't compile.
auto foo = parent(std::move(wantToKeepThis)); // Object gone, don't want this.
auto foo = parent(other_class(wantToKeepThis)); // OK, copied, parent gets rvalue.

【讨论】:

  • 我不希望更改父级,但您使用 && 的想法以及 Alan Birtles 的构造函数委托的想法可能是答案。
【解决方案3】:

创建一个辅助函数来构造可以添加排序的父级:

parent make_parent(::other_class &&oth) {
    auto sf = some_function(oth.some_property);
    return parent(sf, std::move(oth));
}

some_class(::other_class oth) :
    parent(make_parent(std::move(oth))
{}

【讨论】:

  • 你甚至可以用 lambda 来编写它。
  • 我想 lambda 在这种情况下看起来会非常模糊。此外,无论如何,您需要在代码中留出一些空间来让 cmets 了解这里发生的事情。
  • 几乎可以工作,但如果构造函数受到保护,并且类可能是 non-copyable-nor-movable 怎么办?
猜你喜欢
  • 2021-08-21
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
  • 2018-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多