簡體   English   中英

用戶定義的左值引用類型成員的移動構造函數

[英]User-defined move constructor for member of lvalue reference type

我正在使用具有右值引用但不支持默認move構造函數的編譯器上的move語義。 我想生成類似以下包裝器類的東西,即使template參數是左值引用,該類也可以工作。 但是,這種簡單的方法無法編譯,因為它嘗試從int初始化int&。

#define USER_DEFINED   0

template <typename T>
struct Wrapper
{
    Wrapper(T t)
        : m_t(t)
    {
    }

    Wrapper(const Wrapper&) = delete;
    Wrapper& operator=(const Wrapper&) = delete;

#if USER_DEFINED
    Wrapper(Wrapper&& w)
        : m_t(std::move(w.m_t))
    {
    }
#else
    Wrapper(Wrapper&&) = default;
#endif

private:
    T m_t;
};

int main()
{
    int i = 0;
    Wrapper<int&> w1 = Wrapper<int&>(i);
    Wrapper<std::string> w2 = Wrapper<std::string>("text");
}

顯而易見的解決方案是擁有兩個move構造函數,一個用於左值引用,另一個用於所有其他類型。 例如:

template <typename U = T>
Wrapper(typename std::enable_if<!std::is_lvalue_reference<U>::value, Wrapper>::type&& w)
    : m_t(std::move(w.m_t))
{
}

template <typename U = T>
Wrapper(typename std::enable_if<std::is_lvalue_reference<U>::value, Wrapper>::type&& w)
    : m_t(w.m_t)
{
}

那么這是走的路嗎? 也許enable_if<>的表達式應該更通用嗎? 還是我可以使用不同於std :: move()的東西,並為所有類型使用一個單一的構造函數?

好的,這是我認為可以按您需要的解決方案,但是我必須承認我不完全了解它是如何做到的。

我所做的最重要的更改是在move構造函數中將std::move替換為std::forward<T> 我還添加了一個移動賦值運算符,但這是我不了解的事情:除非其他地方都需要std::move而不是std::forward 最后,我還向您的接受T構造函數添加了一個std::forward ,因此它不會為其參數創建兩個副本。 實際上要求我們接受按值的T ,並在此處使用std::forward 如果T是引用,則const T&T&&重載將失敗,因為T&&還將折疊為左值引用,並且重載變得不明確。

#include <iostream>
#include <utility>

template <typename T>
class Wrapper
{

public:

  Wrapper(T t) : m_t {std::forward<T>(t)}
  {
  }

  Wrapper(Wrapper&& w) : m_t {std::forward<T>(w.m_t)}
  {
  }

  Wrapper(const Wrapper&) = delete;

  Wrapper&
  operator=(Wrapper&& w)
  {
    if (this != &w)
      this->m_t = std::move(w.m_t);
    return *this;
  }

  Wrapper&
  operator=(const Wrapper&) = delete;

private:

  T m_t;
};

現在,讓我們將其用於具有儀器類型的測試驅動器,以使我們了解發生了什么情況。

struct A
{
  A ()
  {
    std::cerr << "A was default-constructed" << std::endl;
  }

  A (const A&)
  {
    std::cerr << "A was copy-constructed" << std::endl;
  }

  A (A&&)
  {
    std::cerr << "A was move-constructed" << std::endl;
  }

  A&
  operator=(const A&)
  {
    std::cerr << "A was copy-assigned" << std::endl;
    return *this;
  }

  A&
  operator=(A&&)
  {
    std::cerr << "A was move-assigned" << std::endl;
    return *this;
  }

  ~A ()
  {
    std::cerr << "A was destroyed" << std::endl;
  }
};

int main()
{
  A a {};
  Wrapper<A> val1 {a};
  Wrapper<A> val2 {std::move(val1)};
  val1 = std::move(val2);
  Wrapper<A&> ref1 {a};
  Wrapper<A&> ref2 {std::move(ref1)};
  ref1 = std::move(ref2);
}

與GCC 4.9.1一起編譯(整個練習實際上是毫無意義的,因為它支持開箱即用的所有動作。)並且關閉了所有優化功能,輸出如下(添加了注釋)。

A was default-constructed  ; automatic variable a in main
A was copy-constructed     ; copied as Wrapper<A>'s constructor argument
A was move-constructed     ; forwarded in Wrapper<A>'s initializer
A was destroyed            ; the moved-away from copy as the constructor returns
A was move-constructed     ; forwarded in Wrapper<A>'s move-constructor
A was move-assigned        ; move-assignment operator in Wrapper<A>
A was move-assigned        ; not sure about this one... (apparently caused by the Wrapper<A&> move-assignment)
A was destroyed            ; the sub-object of val2
A was destroyed            ; the sub-object of val1
A was destroyed            ; the automatic variable a in main

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM