2017-01-13 146 views
0

我有一個C++框架,我提供給我的用戶,他們應該使用我用自己的實現編寫的模板包裝作爲模板類型。 包裝器充當RAII類,它包含一個指向用戶類實現的指針。 爲了使用戶的代碼乾淨整潔(在我看來),我提供了一個轉換操作符,它將包裝器轉換爲它所包含的指針。這種方式(以及其他一些重載)用戶可以使用我的包裝,就好像它是一個指針(很像shared_ptr)。C++ std ::移動指針

我遇到了一個角落的情況,用戶調用一個函數,該函數使用指向他的實現類的指針,在我的包裝上使用std :: move。這裏是什麼樣子的例子:

#include <iostream> 
using namespace std; 

struct my_interface { 
    virtual int bar() = 0; 
}; 

template <typename T> 
struct my_base : public my_interface { 
    int bar() { return 4; } 
}; 

struct my_impl : public my_base<int> {}; 

template <typename T> 
struct my_wrapper { 
    my_wrapper(T* t) { 
     m_ptr = t; 
    } 

    operator T*() { 
     return m_ptr; 
    } 

private: 
    T* m_ptr; 
}; 

void foo(my_interface* a) { 
    std::cout << a->bar() << std::endl; 
} 


int main() 
{ 
    my_impl* impl = new my_impl(); 
    my_wrapper<my_impl> wrapper(impl); 
    foo(std::move(wrapper)); 
    //foo(wrapper); 

    return 0; 
} 

[這是ofcourse只是案件的一個例子,有在包裝更多的方法,但我敢肯定,在這裏不發揮作用在這種情況下]

用戶,如我所料,如果在包裝上調用std :: move,那麼在調用foo之後,包裝將是空的(或者至少像被移動一樣進行修改) ,但實際上在foo之前調用的唯一方法是演員。

有沒有一種方法,使兩個調用使用和不std::move打電話時foo即區分之間的調用foo

編輯 得益於鳴叫鴨的評論,我發現一種方式,my_wrapper知道哪些電話是必需的,但我真的不知道這是一起去的最佳方法,並讚賞這樣的評論,以及:

而不是以前的類型轉換操作符的使用以下兩種:

operator T*() & { 
    return m_ptr; 
} 

operator T*() &&{ 
    //Do something 
    return m_ptr; 
} 

現在operator T*() &&用的std ::移動和operator T*() &打電話時被稱爲沒有它調用時被調用。

+0

您是指在呼叫站點還是被調用者區分? –

+0

被調用者的意思是'my_impl'類型? – ZivS

+3

用戶的期望是不合理的。如果你對某個東西調用了'std :: move',並將它傳遞給一個沒有佔用移動對象的函數,那麼期望這個對象是空的或者更改是不合理的。例如:'std :: shared_ptr foo; ... std :: move(foo) - > bar();'這不應該改變'foo',因爲'bar'不佔有。如果'bar(std :: move(foo));'也是如此。 'std :: move'只是給被調用的函數*權限*來移動對象,如果沒有指定它,它不會強制它移動它。 –

回答

3

用戶,正如我預計,如果性病::此舉被稱爲在包裝,然後調用後foo的包裝將是空的(或至少修改,如果它被移動)

你的期望是錯的。只有在移動發生時纔會對其進行修改,即如果轉移某種資源的所有權。但調用foo不會做那樣的事情,因爲它只是訪問包裝器內的指針。調用std::move不會做任何事情,除非將其參數轉換爲右值,而不會改變它。某些通過引用接受右值的函數可能會對其進行修改,因此std::move啓用了該功能,但它本身不會這樣做。如果您沒有將右值傳遞給這樣的函數,那麼不會發生修改。

如果你真的讓它空,您可以添加過載做到這一點:

template<typename T> 
void foo(my_wrapper<T>&& w) { 
    foo(static_cast<my_interface*>(w)); 
    w = my_wrapper<T>{}; // leave it empty 
} 

可是......爲什麼呢?爲什麼要這樣做?

,如果你做的包裝不會留空:

my_wrapper<my_impl> w(new my_impl); 
my_wrapper<my_impl> w2 = std::move(w); 

而不是由空:

my_wrapper<my_impl> w(new my_impl); 
my_wrapper<my_impl> w2; 
w2 = std::move(w); 

如果複製一個右值包裝不留空,爲什麼應該簡單地訪問它的成員將它留空?這是沒有意義的。

即使你包裝有一個移動構造函數和移動賦值操作符,這樣上面的例子離開w空,仍然並不意味着訪問一個右值對象的成員應該修改的對象。爲什麼operator T*轉換是對左值還是右值做了邏輯區分?

(另外,你真的能肯定的是同時具有從包裹指針類型隱式轉換是一個好主意?提示:這不是一個好主意,一般更願意讓你轉換明確,特別。如果您正在處理指向動態分配對象的指針)

+1

一年後,閱讀你的答案是有道理的,我可以說,第一次閱讀這個答案對我來說感覺不對,但現在我知道這只是因爲我沒有完全理解移動語義。謝謝 :) – ZivS