Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/126.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
传递值和std::move over pass by reference的优点 我现在正在学习C++,尽量避免养成坏习惯。 据我所知,clang tidy包含许多“最佳实践”,我尽可能地坚持这些实践(尽管我还不一定理解为什么它们被认为是好的),但我不确定我是否理解这里推荐的内容_C++ - Fatal编程技术网

传递值和std::move over pass by reference的优点 我现在正在学习C++,尽量避免养成坏习惯。 据我所知,clang tidy包含许多“最佳实践”,我尽可能地坚持这些实践(尽管我还不一定理解为什么它们被认为是好的),但我不确定我是否理解这里推荐的内容

传递值和std::move over pass by reference的优点 我现在正在学习C++,尽量避免养成坏习惯。 据我所知,clang tidy包含许多“最佳实践”,我尽可能地坚持这些实践(尽管我还不一定理解为什么它们被认为是好的),但我不确定我是否理解这里推荐的内容,c++,C++,我使用了教程中的这个类: class Creature { private: std::string m_name; public: Creature(const std::string &name) : m_name{name} { } }; 这导致了clang tidy的建议,即我应该通过值而不是引用传递,并使用std::move。 如果我这样做,我会得到建议,让name成为一个引用(以确保它不会每次被复制),并且警告st

我使用了教程中的这个类:

class Creature
{
private:
    std::string m_name;

public:
    Creature(const std::string &name)
            :  m_name{name}
    {
    }
};
这导致了clang tidy的建议,即我应该通过值而不是引用传递,并使用
std::move
。 如果我这样做,我会得到建议,让
name
成为一个引用(以确保它不会每次被复制),并且警告
std::move
不会有任何效果,因为
name
是一个
const
,所以我应该删除它

我不收到警告的唯一方法是将
const
一起删除:

Creature(std::string name)
        :  m_name{std::move(name)}
{
}
这似乎合乎逻辑,因为
const
的唯一好处是防止弄乱原始字符串(这不会发生,因为我通过值传递)。 但我继续读:

尽管请注意,在标准库中,移动意味着从对象移动的对象处于有效但未指定的状态。这意味着,在这样一个操作之后,从对象中移出的值只应被销毁或分配一个新值;否则访问它会产生一个未指定的值

现在想象一下这个代码:

std::string nameString("Alex");
Creature c(nameString);
因为
nameString
是按值传递的,
std::move
只会使构造函数内的
name
无效,而不会触及原始字符串。但这有什么好处呢?不管怎样,内容似乎只被复制了一次——如果我在调用
m_name{name}
时通过引用传递,如果我在传递它时通过值传递(然后它被移动)。我知道这比按值传递而不使用
std::move
(因为它会被复制两次)要好

所以有两个问题:

  • 我是否正确理解这里发生的事情
  • 使用
    std::move
    而不是通过引用调用
    m_name{name}
    ,有什么好处吗
  • 我是否正确理解这里发生的事情
  • 使用
    std::move
    而不是通过引用调用
    m_name{name}
    ,有什么好处吗
  • 易于掌握的函数签名,无需任何额外的重载。签名立即显示参数将被复制-这使调用方不必担心
    const std::string&
    引用是否会存储为数据成员,以后可能会成为悬空引用。而且,在将右值传递给函数时,无需重载
    std::string&&name
    const std::string&
    参数,以避免不必要的副本。传递左值

    std::string nameString("Alex");
    Creature c(nameString);
    
    对于按值获取参数的函数,将导致一次复制和一次移动构造。将右值传递给同一函数

    std::string nameString("Alex");
    Creature c(std::move(nameString));
    
    导致两步结构。相反,当函数参数为
    const std::string&
    时,即使在传递右值参数时,也将始终存在副本。这显然是一个优势,只要参数类型移动构造成本低(这是
    std::string
    的情况)

    但有一个缺点需要考虑:对于将函数参数分配给另一个变量(而不是初始化它)的函数,推理不起作用:


    将导致在重新分配资源之前,
    m_name
    引用的资源被解除分配。我推荐在现代C++中阅读41项,也可以。

    <强> > < <强> >不是这里唯一的变量,<强> > < <强> >使两者之间有很大差异。

    <>在C++中,我们有这个“成语”,用于在“强”>rValue/String(例如<代码> >亚历克斯字符串文字中构造临时STD::/String 或,这导致<强> 0个拷贝<强> > <代码> STD::String < /代码>(对于右值参数,该类型甚至不必是可复制构造的),并且只使用
    std::string
    的move构造函数

    • 传递的左值绑定到
      name
      ,然后复制到
      m_name

    • 传递的右值绑定到
      name
      ,然后复制到
      m\u name


    • 传递的左值被复制到
      name
      ,然后移动到
      mu name

    • 传递的右值被移动到
      name
      ,然后移动到
      mu name


    • 传递的左值绑定到
      name
      ,然后复制到
      m_name

    • 传递的右值绑定到
      rname
      ,然后移动到
      m\u name


    由于移动操作通常比拷贝快,(1)(0)好,如果你通过了很多临时操作。(2)在拷贝/移动方面是最佳的,但需要代码重复

    通过完美转发可以避免代码重复:

    /*(3)*/
    模板=0
    >
    生物(T&&name):m_name{std::forward(name)}{
    
    您可以选择约束
    T
    ,以限制此构造函数可以实例化的类型的域(如上所示)。C++20旨在简化此操作



    在C++17中,prvalues受影响,如果适用,将参数传递给函数时,prvalues会减少复制/移动的数量。

    传递值和移动方法优于传递-(rv)引用有几个缺点:

    • 它会导致生成3个对象
      void setName(std::string name)
      {
          m_name = std::move(name);
      }
      
      /* (0) */ 
      Creature(const std::string &name) : m_name{name} { }
      
      /* (1) */ 
      Creature(std::string name) : m_name{std::move(name)} { }
      
      /* (2) */ 
      Creature(const std::string &name) : m_name{name} { }
      Creature(std::string &&rname) : m_name{std::move(rname)} { }
      
      /* (3) */
      template <typename T,
                std::enable_if_t<
                    std::is_convertible_v<std::remove_cvref_t<T>, std::string>, 
                int> = 0
               >
      Creature(T&& name) : m_name{std::forward<T>(name)} { }
      
      #pragma pack(push, 1)
      template<class T>
      class CopyOrMove{
      public:
          CopyOrMove(T&&t):m_move(&t),m_isMove(true){}
          
          CopyOrMove(const T&t):m_reference(&t),m_isMove(false){}
          bool hasInstance()const{ return m_isMove; }
          const T& getConstReference() const {
              return *m_reference;
          } 
          T extract() && {
            if (hasInstance())
                  return std::move(*m_move);
            else
                  return *m_reference;
          }
          
          void fastExtract(T* out) && {
            if (hasInstance())
                  *out = std::move(*m_move);
            else
                  *out = *m_reference;
          }  
      private:
          union
          {
              T* m_move;
              const T* m_reference;
          };
          bool m_isMove;
      };
      #pragma pack(pop)
      
      Creature(CopyOrMove<std::string> name)
          :  m_name{std::move(name.extract())}
      {
      }