2017-10-12 122 views
1

考慮:爲什麼這個用戶定義的轉換沒有完成?

template<typename T> 
struct Prop 
{ 
    T value; 
    operator T() { return value; } 
}; 

int main() 
{ 
    Prop<float> p1 { 5 }; 
    Prop<std::vector<float>> p2 { { 1, 2, 3 } }; 

    float f1 = p1;    // Works fine 
    float f2_1_1 = p2.value[0]; // Works fine 
    float f2_1_2 = p2[0];  // Doesn't compile 

    return 0; 
} 

爲什麼不標明爲行編譯?它不應該使用提供的轉換運算符執行隱式轉換爲std::vector<>,以便可以找到[]

在這個網站上有(許多)其他問題要求對這個問題的變化,但我找不到一個,我認爲適用於此。它與std::vector是模板有關嗎?

+2

表達式'p2 [0]'實際上與'p2.operator [](0)'相同(應該可以從錯誤消息中推斷出來)。而'Prop'類沒有'operator []'函數。 –

+0

所以問題是,p2 [0]被認爲是一個表達式,對吧?因爲如果p2被認爲是一個表達式,轉換爲std :: vector將被執行,然後.operator [](0)將被調用,對吧?那麼是否有一種便捷的方式讓Prop以我想要的方式行事 - 即作爲T型某些值的透明容器以及一些屬性(因爲在我的真實代碼中,Prop有額外的成員)。 – Roel

+0

像這樣包裝數據的常用方法是重載成員訪問操作符' - >'或解引用操作符'*'。然後你可以在(0)'或者'(* p2)[0]''上做'p2->。它比較麻煩,但仍比'static_cast'好。 –

回答

4

隱式轉換不被認爲是對的成員函數調用的對象,包括下標操作過載。

考慮後果如果允許的話:每次任何未聲明的成員函數都是這樣調用的,編譯器必須找出該對象可以轉換的所有類型(注意,任何其他不相關的類型都可以有一個轉換構造函數),並檢查是否聲明瞭缺少的成員函數。更不用說這對代碼讀者會有多混淆(轉換可能在轉換運算符的情況下顯而易見,但不在轉換構造函數的情況下,並且據我所知,它們的處理方式不同)。

那麼,有沒有notationally方便的方式來獲得道具的行爲我想要的方式

你必須定義一個成員函數爲每個你想傳遞通矢量的成員函數通過透明。以下標操作符爲例:

auto operator[](std::size_t pos) { 
    return value[pos]; 
} 
auto operator[](std::size_t pos) const { 
    return value[pos]; 
} 

問題當然是所有包裝的成員函數都必須顯式聲明。另一個問題是類型取決於T的論點。例如,vector::operator[]使用vector::size_type,可能不會爲您可能使用的所有T定義(當然不適用於float)。在這裏,我們做出妥協並使用std::size_t

創建這種「透明」包裝的一種較不費力的方式是繼承。一個公開繼承的模板會自動擁有父代的所有成員函數,並且可以隱式轉換爲它,並且可以通過父類型的指針和引用來引用。但是,這種方法的透明度有點問題,主要是因爲~vector不是虛擬的。

私有繼承允許同一包裝爲您的會員的方法,但用更漂亮的語法:

template<typename T> 
struct Prop : private T 
{ 
    using T::operator[]; 
    using T::T; 
}; 

注意繼承方法阻止您使用基本類型爲T。此外,它使隱式轉換不可能(即使使用轉換運算符),因此您不能在T的免費函數中使用Prop作爲T


PS。請注意,您的轉換運算符會返回一個值,因此必須複製該向量,這可能不合意。考慮提供參考版本:

operator T&&()&&   { return *this; } 
operator T&()&    { return *this; } 
operator const T&() const& { return *this; } 
+0

這很有道理,謝謝。 – Roel

+0

非公有繼承,並使用'using'發佈接口的所需部分可能是繼承問題的解決方案。 – Angew

+0

「這種方法的問題當然是,那麼你不能只包裝任何類型,只能包裝那些提供包裝函數的類型。」不完全的。由於包裝是一個模板,除非被調用,否則函數將不會被實例化。所以你可以*包裝不支持'[]'的類型,在這種情況下你不能將'[]'應用於包裝器。 – Angew

1

同樣的方式,任何

p2.size(); 
p2.begin(); 
p2.push_back(24); 
// etc. 

沒有意義編譯

p2[0]; 

相當於擁有了

p2.operator[](0); 

沒有按」沒有道理編輯\


如果你想要這種行爲(即,對於Prop<T>借用T成員)Bjarne有一個C++提議將點運算符添加到語言中。我的工作方式與運營商->適用於智能指針相同。 AFAIR有很多爭議,所以我不會屏住呼吸。

A bit of background for the operator dot proposal—Bjarne Stroustrup

Operator Dot (R3) - Bjarne Stroustrup, Gabriel Dos Rei

Smart References through Delegation: An Alternative to N4477's Operator Dot - Hubert Tong, Faisal Vali

Alternatives to operator dot - Bjarne Stroustrup

相關問題