5

我發現一些線程暗示這不能完成,但沒有一個使用完全相同的運算符和條件組合,所以我想問一些更具體的問題。希望這意味着對某個人來說這是一個快速而簡單的答案......這種或那種方式!是否有可能使'=`更喜歡通過(刪除)複製分配進行轉換賦值?

考慮一個例子代理類,做存儲的較大塊中管理的值 - 在這個過於簡單,但代表性的例子:

class SomeProxyThing { 
    std::uint32_t storage; 

public: 
    operator std::uint16_t() const 
    { 
     return storage & 0x0000FFFF; 
    } 

    SomeProxyThing &operator=(std::uint16_t const value) 
    { 
     storage &= 0xFFFF0000; 
     storage |= value; 
    } 
}; 

我想所有分配通過定義用戶工作operator s。用戶應該只能夠進入或退出'暴露'類型,在這種情況下std::uint16_t。我可能會使用各種代理類類型,並希望它適用於所有這些類型。理想情況下,對於任何類型的組合,我只需鍵入someProxy = anotherProxy,然後讓編譯器完成剩下的工作。

但是,當作業的左側和右側有相同或繼承相關的類型時,默認的複製賦值運算符 - 當然會與此目標衝突。它會複製整個storage,從而破壞uint32_t的另一半 - 而不是僅根據需要複製「暴露」值。正確的!對於大多數情況。但是,即使LHS和RHS類型相同,我也想要一種「按轉換分配」的方式。爲了避免這種情況,我可以:

  • 重新定義拷貝賦值運算符執行「代理」複製使用用戶定義的operator秒 - 這是我一直在做什麼,但似乎有點哈克一樣,打破可複製的struct的狀態 - 我需要保留。它仍然memcpy() s 無論如何g++,但我想定義的行爲。
  • = delete複製賦值運算符(我們現在可以對TC類型進行操作)。但作業仍然嘗試使用它並拋出一個編譯錯誤 - 因爲delete意味着'如果我是所選擇的超載而中止一個錯誤',而不是'將我排除在重載解析之外'。爲了解決這個問題,我必須明確地告訴編譯器使用轉換操作符,並從其結果分配:
SomeProxyThing a, b; 
a = 42; 
b = static_cast<std::uint16_t>(a); 
// a.k.a. 
b.operator=(a.operator std::uint16_t()); 

目前似乎沒有一種方法來告訴編譯器「忽略您首選的超載所產生的任何錯誤,並選擇下一個最好的錯誤'。在那兒?更一般地說,在這種情況下,有沒有什麼辦法/破解/可怕的kludge到強制編譯器自動使用/更喜歡某些operator s?

換句話說,理想情況下,在

SomeProxyThing a, b; 
a = 42; 
b = a; 

b = a;會真正做到這一點:

b = static_cast<std::uint16_t>(a); 
// a.k.a. 
b.operator=(a.operator std::uint16_t()); 

沒有我不必手動鍵入此,使用static_cast,或實現命名的get/set方法。理想情況下,我希望對任何這樣的代理進行讀取/寫入,使其看起來像寫入代碼中的基本類型的讀取/寫入,全部使用=

我強烈懷疑這是不可能的......但確認就好了!

+4

似乎是在您要求的矛盾:當副本將被做,你不想默認的拷貝賦值運算符的行爲,但你仍然希望類是平凡可複製。你不能同時擁有; trivially可複製意味着(其中包括)默認操作是正確的類。 – bogdan

+0

@bogdan當然。我意識到,我想從一個相同類型的賦值通過轉換運算符來賦值,這是相當自相矛盾的,所以我懷疑這是可能的 - 只是想真正確認。在我目前的項目中,我使用了這個主題的幾個變體,這些變體都通過轉換運算符正確指定給每個其他變量 - 只有當LHS和RHS具有相同(或派生)類型時,纔會出現此問題。我真的只想懶惰,對所有組合使用相同的語法,但我的直覺是它不能完成。 –

+0

'memcpy'ability是我真正的目標,而不是簡單的複製分配,但標準認爲它們與內在相關,原因很明顯。有[線程討論可能的改進,以_trivially copyable_和建議「的memcpy」 - 能夠類型的新類別(https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/wphImiqfX7Y)這可能會增加更多的細微差別,但我不知道實際上已經去過任何地方。 –

回答

-1

編譯時間類型匹配/不匹配可以通過標準:: enable_if來控制。 隱式類型轉換可以通過顯式關鍵字禁用。 可以顯式刪除所有的複製和移動構造函數,以避免複製和默認的構造函數可以顯式標記爲默認值。 編輯:早期的答案考慮到這個問題的第一部分是「用戶應該只能夠在通過或擺脫‘暴露’型」,讓所有的轉換必須是明確的,但是完成,你可以定義一個平凡的答案可複製類,並使用您的代理類中 可能是你想要的東西:

#include <cstdint> 
#include <type_traits> 
struct copyable{ 
    std::uint32_t number = 0x0; 
}; 
class SomeProxyThing { 

public: 
    explicit operator std::uint16_t() const 
    { 
     return storage.number & 0x0000FFFF; 
    } 
template <typename T, typename std::enable_if<std::is_same<T, std::uint16_t>::value, int>::type=0> 
    SomeProxyThing& operator=(T value) 
    { 
     storage.number &= 0xFFFF0000; 
     storage.number |= value; 
     return *this; 
    } 

    SomeProxyThing()=default; 
    SomeProxyThing(const SomeProxyThing&)=delete; 
    SomeProxyThing(SomeProxyThing&&)=delete; 
    SomeProxyThing& operator=(const SomeProxyThing& other) { 
     this->storage.number = static_cast<std::uint16_t>(other.storage.number); 
    } 
    SomeProxyThing& operator=(SomeProxyThing&& other) { 
     this->storage.number = static_cast<std::uint16_t>(other.storage.number); 
    } 

private: 
    copyable storage; 
}; 
int main() 
{ 
    SomeProxyThing a, b; 
    a = static_cast<std::uint16_t>(43); 
    b = a; 
} 
+0

......你真的在名稱之前閱讀了我的問題 - 刪除了一些基本概念?如果類型匹配,禁用轉換/賦值恰恰是**問題的** **相反**:我具體詢問是否有一種方法可以使** **優先於拷貝分配。我知道所有關於'delete'的情況 - 我證明了我是如何嘗試copy-assign的,它比'enable_if'更具可讀性 - 但並沒有解決問題:它只能防止我意外地使用複製分配,但不能解決問題贊成任何選擇。代碼中的成功/失敗情況是_exactly_我​​不希望看到 –

+0

感謝您的努力。但是,「編輯:早期回答」考慮了「用戶應該只能傳入或傳出'暴露'類型的問題的第一部分」,因此所有轉換都必須是明確的「,它不會遵循。同樣,理想情況下,所有轉換都是**隱式** _。這就是我所做的,在所有情況下工作都很好,除非LHS和RHS是相關類型。然而'但是要完成答案,您可以定義一個可以複製的類並在代理類中使用它'這是幹什麼用的?我還有一個代理類,不是可以複製的,除非現在維護起來很困難! –

+0

@underscore_d我也更改了代碼。現在可以簡單地複製存儲對象,這樣可以避免「不可能」的懷疑...... – rahnema1

1

你可以這樣做:

#include <stdint.h> 
#include <iostream> 
#include <type_traits> 

using namespace std; 

class Proxy_state 
{ 
protected: 
    uint32_t storage; 
public: 
    // Access to the bytes 
}; 

static_assert(is_trivially_copyable<Proxy_state>::value, "!"); 

class Some_proxy_thing 
    : public Proxy_state 
{ 
private: 

public: 
    operator std::uint16_t() const 
    { 
     return storage & 0x0000FFFF; 
    } 

    auto operator=(uint16_t const value) 
     -> Some_proxy_thing& 
    { 
     clog << "=(uint16_t)" << endl; 
     storage &= 0xFFFF0000; 
     storage |= value; 
     return *this; 
    } 

    auto operator=(Some_proxy_thing const& value) 
     -> Some_proxy_thing& 
    { return operator=(static_cast<uint16_t>(value)); } 
}; 

static_assert(not is_trivially_copyable<Some_proxy_thing>::value, "!"); 

auto main() 
    -> int 
{ 
    Some_proxy_thing a{}; 
    Some_proxy_thing b{}; 
    const Some_proxy_thing c = b; 

    a = c; 

    a = 123; 
    a = b; 
} 

這裏所有三個任務的輸出(標準錯誤流)=(uint16t)

+0

你仍然得到隱式聲明的副本分配;它只是在重載分辨率上丟失了模板。 'const Some_proxy_thing&c = b; a = c;'仍然不起作用。 –

+0

謝謝阿爾夫,有趣的想法 - 我想知道模板是否可以幫助,但沒有想到他們可以任何方式。 @ T.C。是正確的,儘管我可以生活在大多數情況下不能從''contst'分配,因爲我不傾向於使用'const'版本的這種對象。然而,更重要的是 - 這個'template'不是真的只是爲'SomeProxyThing &&'生成一個用戶提供的移動賦值操作符,從而再次打破微不足道的可複製性?所有的跡象都指向這一切都是不可能的,儘管我很樂意在思考中被證明是錯誤的! –

+0

@ T.C。 (和underscore_d):謝謝,修正了這個問題。我認爲這是儘可能好的。否則,我認爲你必須改變要求。 –

相關問題