【问题标题】:Why this code is fast for char *?为什么这段代码对 char * 来说很快?
【发布时间】:2015-08-26 01:32:06
【问题描述】:

在 1:15:26 在this talk by Sutter 中,出现了如下代码,

class employee{
 std::string name_;

public:
template<class String, class=
 std::enable_if_t< !std::is_same<std::decay_t<String>, std::string>::value > >
void set_name(String && name)
   noexcept(std::isnothrow_assignable<std::string &, String>::value)
  {
    name_ = std::forward<String>(name);
  }
}

我知道std::forward 是如何工作的,如果name 是一个左值,name_ 将得到副本构造;如果name 是右值,name_ 将被构造移动。但是在幻灯片中它还说Optimized to steal from rvalues (and more),还有什么?

后来它显示这段代码似乎是所有四个实现中最快的,尤其是对于char *,任何人都有耐心浏览这段代码并解释还有什么正在优化以及为什么它是最快的,特别是在char *的情况?

【问题讨论】:

  • 注意他所比较的函数以一种或另一种形式采用std::string。如果您的目标只是为char * 编写快速的代码,那么您没有必须使用完美转发。

标签: c++ c++11


【解决方案1】:

首先,请注意代码包含一个拼写错误,即使删除了拼写错误,enable_if 约束也不会执行所讨论的操作;特别是该功能不适用于char*,因此显然它不是char* 最快的。你可以看到我问过这个here 的一个问题,以及一个“更正”的完美转发设置器(以及 Howard Hinnant 对该版本的认可)。

template <class String>
auto set_name(String&& name) 
-> decltype(name_ = std::forward<String>(name), void()) {
    name_ = std::forward<String>(name);
}

用于演示的基准是在 employee 上反复调用 set_name,以显示成员 string 可以重用其容量而不是每次迭代都分配内存的情况。基准中显示的高条和短条之间的差异是重用容量与每次迭代进行分配之间的差异。

正确的完美转发器使用char* 快速的原因是因为char* 模板的实例化只是转发了char*,而不是需要构造一个string 参数来调用set_name一个实际的string 对象参数。 setter 中的赋值很快,因为 string 实现了 operator=(char*),它可以将高效的 memcpy 放入现有的存储中,并且避免了额外的分配。

所以Optimized to steal from rvalues (and more) 的说法是因为完美转发除了转发什么都不做。如果它获得一个右值,它不仅会传递一个右值,而且它也不会转换类型,这意味着“转发者”实现的优化,例如 stringoperator=(char*) 也会暴露出来。

【讨论】:

  • 我明白了。赞成“如果它获得一个右值,它不仅会通过一个右值,而且它也不会不必要地转换类型”。
  • 你的set_name() 没有返回任何东西,它有一个返回类型
  • @BЈовић:返回类型(在 SFINAE 之后)是decltype(void()) 所以void
  • @bames53,我现在差不多明白了,name_ = std::forward&lt;String&gt;(name) 里面decltypeuse SFINAE to detect operator support,但是name_ = std::forward&lt;String&gt;(name) 会被执行两次?一次在decltype,第二次在函数体内?
  • @Allanqunzi 不,decltype 内的表达式不被计算
猜你喜欢
  • 2011-05-27
  • 2016-09-17
  • 1970-01-01
  • 2018-05-05
  • 2019-12-29
  • 1970-01-01
  • 1970-01-01
  • 2013-05-13
  • 2014-09-14
相关资源
最近更新 更多