gpt4 book ai didi

c++ - 是否值得添加一个支持 move 的二传手?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:10:03 25 4
gpt4 key购买 nike

这篇文章有点啰嗦,所以在开始之前我想弄清楚我要问的是什么:您是否已将启用 move 的 setter 添加到您的代码中并且您是否发现它值得付出努力?我发现的预期行为中有多少可能是特定于编译器的?

我在这里关注的是在我设置复杂类型的属性的情况下是否值得添加启用 move 的 setter 函数。在这里,我有启用 move 的 BarFoo,它有一个可以设置的 Bar 属性。

class Bar {
public:
Bar() : _array(1000) {}
Bar(Bar const & other) : _array(other._array) {}
Bar(Bar && other) : _array(std::move(other._array)) {}
Bar & operator=(Bar const & other) {
_array = other._array;
return *this;
}
Bar & operator=(Bar && other) {
_array = std::move(other._array);
return *this;
}
private:
vector<string> _array;
};

class Foo {
public:
void SetBarByCopy(Bar value) {
_bar = value;
}
void SetBarByMovedCopy(Bar value) {
_bar = std::move(value);
}
void SetBarByConstRef(Bar const & value) {
_bar = value;
}
void SetBarByMove(Bar && value) {
_bar = std::move(value);
}
private:
Bar _bar;
};

一般来说,过去我使用 const-ref 作为非内置类型的 setter 函数。我查看的选项是按值传递然后 move (SetByMovedCopy),通过 const-ref 传递然后复制 (SetByConstRef) 最后通过 r-value- 接受ref 然后 move (SetByMove)。作为基准,我还包括按值传递然后复制 (SetByCopy)。 FWIW,如果同时包含按值传递和 r-value-ref 重载,编译器会提示歧义。

在 VS2010 编译器的实验中,这是我发现的:

Foo foo;
Bar bar_one;

foo.SetByCopy(bar_one);
// Bar::copy ctor called (to construct "value" from bar_one)
// Foo::SetByCopy entered
// Bar::copy operator= called (to copy "value" to _bar)
// Foo::SetByCopy exiting
// Bar::dtor called (on "value")

value是从bar_one复制构造的,然后value被复制到barvalue 被破坏并产生破坏完整对象的任何成本。执行了2次复制操作。

foo.SetByMovedCopy(bar_one);
// Bar::copy ctor called (to construct "value" from bar_one)
// Foo::SetByCopy entered
// Bar::move operator= called (to move "value" into _bar)
// Foo::SetByCopy exiting
// Bar::dtor called (to destruct the moved "value")

value 是从 bar_one 复制构造的,然后 value 被 move 到 _bar 中,然后是去内脏的 value 在函数退出后被销毁,大概成本较低。 1 次复制和 1 次 move 操作。

foo.SetByConstRef(bar_one);
// Foo::SetByConstRef entered
// Bar::copy operator= called (to copy bar_one into _bar)
// Foo::SetByConstRef exiting

bar_one 被直接复制到 _bar 中。 1次复制操作。

foo.SetByMove(std::move(bar_one))
// Foo::SetByMove entered
// Bar::move operator= called (to move "value" into _bar)
// Foo::SetByMove exited

bar_one 直接 move 到 _bar 中。 1 次 move 操作。


所以在这种情况下,const-ref 和 move 版本是最有效的。现在,更重要的是,我想要做的是这样的事情:

void SetBar(Bar const & value) { _bar = value; }
void SetBar(Bar && value) { _bar = std::move(value); }

我发现这里发生的情况是,如果您调用 Foo::SetBar,编译器会根据您传递的是左值还是右值来选择函数。您可以通过这样调用 std::move 来强制执行此问题:

foo.SetBar(bar_one); // Const-ref version called
foo.SetBar(Bar()); // Move version called
foo.SetBar(std::move(bar_one)); // Move version called

一想到要添加所有这些 move setter 我就不寒而栗,但我认为在临时传递给 SetBar 函数的情况下,这可能会带来相当显着的性能提升,并且继续前进我可以获得通过在适当的地方应用 std::move 甚至更多。

最佳答案

另一种选择是模板:

template <typename T>
typename std::enable_if<std::is_assignable<Foo, T>::value>::type set(T && t)
{
foo_ = std::forward<T>(t);
}

这样您就可以匹配任何可转换的和任何值(value)类别。别忘了 #include <type_traits>得到is_assignable . (您不应该省略 enable_if,这样您的函数就不会错误地出现在其他特征检查中。)

关于c++ - 是否值得添加一个支持 move 的二传手?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10692345/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com