2010-12-09 83 views
7

大部分關於成員指針的討論都關注成員所屬類型允許的轉換。我的問題是關於成員類型的轉換。指向數據成員與非成員指針的隱式轉換

struct Base{}; 
struct Derived : public Base{}; 
struct Foo{ Derived m_Derived; }; 

鑑於這些聲明,下面的代碼會產生一個錯誤(2008 MSVC):來自*基地*通常允許

// error C2440: 'initializing' : cannot convert from 'Derived Foo::* ' to 'Base Foo::* ' 
Base Foo::*p = &Foo::m_Derived; 

轉換 - 這裏爲什麼不同?

+1

「通常允許從基礎*轉換爲派生*」 我認爲你有這個落後。 – 2010-12-09 22:33:03

+0

好的。固定。 – Chris 2010-12-09 22:38:59

+0

見http://stackoverflow.com/questions/4295117/pointer-to-member-conversion – icecrime 2010-12-09 22:57:59

回答

0

有趣的問題。數據指針很少使用,我對規則很不熟悉。

但是,我打算把錢歸因於多重繼承。如果Base和Derived使用虛擬繼承,編譯器無法知道任何給定Derived中Base的偏移量,從而無法編譯時偏移,並且將其合法化爲非虛擬遺產。

2

你有反差。

返回類型隱式轉換爲基本類型(逆變)。但是參數隱式轉換爲派生類型(協方差),並且指向成員的類類型可以作爲參數。要看到這一點,讓我們應用Liskov可替代性原則:

Base*的合同是:「我會給你一個基地」(當你對我使用*運營商時)。 Derived*合同是「我會給你一個派生的,這也是一個基地」。

顯然,Derived*可以用來代替Base*。因此存在從Derived*Base*的隱式轉換。

但是考慮指向成員的指針。

int Base::*的合同是:「給我一個基地,我會還給你一個int」(A派生是基礎,所以這些都是OK太) 的int Derived::*的合同是:「給我一個推導我會給你回一個int」(但不是任何舊Base會做,它必須是一個Derived

假設你有一個Base這不是一個Derived。當解引用int Base::*時它會很好地工作,但不能與int Derived*一起使用)。

但是,如果您有 Derived,則可以使用它取消引用 int Base::*int Derived::*。因此,有從 int Base::*int Derived::*

哎呀隱式轉換,我沒有你所說的話,並分析了該成員所屬的類型。

雖然LSP仍然有效。我同意轉換應該是合法的,至少根據類型安全。合同是「給我一個Foo,我會給你一個Derived」,這顯然你可以用它來從FooBase,通過組成一個隱式轉換。所以它很安全。 DeadMG可能在正確的軌道上指出了與基本子對象的關係位置的潛在複雜性,特別是在虛擬繼承中。但是指針成員在解除引用運算符的LHS中處理這些問題,所以他們也可以得出結果。

最終答案可能只是標準並不要求轉換是合法的。

1

@Ben Voigt:你爲什麼挑出你的正確答案?

安全指針轉換是反變換的,這意味着您可以安全地向上轉發,但不能向下轉發。

成員轉換的安全指針是同變異的,這意味着您可以安全地下注,但不能上傳。

原因很容易看到,當你想到它。假設你有一個指向Base的成員的指針,這是Base的偏移量。那麼如果完整的對象是派生的,那麼同一個成員到派生的偏移量是多少?通常它的偏移量完全相同:當然,如果指向Base和Derived的指針是相同地址的話。如果你有多重繼承和虛擬基地,事情會變得更復雜一點:)

事實上,OP的示例代碼就是爲什麼上演不安全的最佳例子:Derived類成員的偏移量可以應用於任何Base,並且指出基本子對象的末尾,如果它實際上是Derived而不是Derived2,那麼這只是確定的。