2011-11-19 81 views
0

您好CRTP相關的編譯器錯誤,上的指針到一個成員函數默認值

同時使基於CRTP,通用包裝調用任意庫函數,我遇到一個問題,我理解有困難。下面是一個非常簡化的代碼來說明問題:

#include <iostream> 

template< typename PValue, typename PDerived > 
class TBase 
{ 
private: 
    typedef TBase TSelf_; 
    typedef PDerived TDerived_; 

protected: 
    typedef PValue TValue_; 

protected: 
    TBase(void) 
    { 
    std::cout << " TBase::TBase() " << std::endl; 
    } 

public: 
    void Foo(void) 
    { 
    std::cout << " TBase::Foo() " << std::endl; 
    } 

    template< typename PType > 
    static void Call(PType /*pSomething*/, void(TDerived_::*pFunction)(void) = &TSelf_::Foo, TDerived_ pDerived = TDerived_()) 
    { 
    (pDerived.*pFunction)(); 
    std::cout << " static TBase::Call(). " << std::endl; 
    } 
}; 

template< typename PValue > 
class TDerived : public TBase< PValue, TDerived<PValue> > 
{ 
    friend class TBase< PValue, TDerived<PValue> > ; 
private: 
    typedef TBase< PValue, TDerived > TBase_; 
    typedef TDerived TSelf_; 
public: 
    TDerived(void) : 
    TBase_() 
    { 
    std::cout << " TDerived::TDerived() " << std::endl; 
    } 
    void Foo(void) 
    { 
    std::cout << " TDerived::Foo() " << std::endl; 
    } 
    void Bar(void) 
    { 
    std::cout << " TDerived::Bar() " << std::endl; 
    } 
}; 

int main(void) 
{ 
TDerived<int>::Call(1); 
TDerived<int>::Call(1, &TDerived<int>::Foo); 
TDerived<int>::Call(1, &TDerived<int>::Bar, TDerived<int>()); 
return (0); 
} 

一切都按照預期編譯和工作。不過,如果我嘗試在TBase::Call(...)使用指針TDerived::Foo()作爲第二個參數的默認參數:

static void Call(PType /*pSomething*/, void(TDerived_::*pFunction)(void) = &TDerived_::Foo, TDerived_ pDerived = TDerived_()) 

編譯器提供了一個語法錯誤......我有一種感覺它關係到編譯器如何解析代碼和它無法找出指向尚未定義(或實例化)類的函數的指針。但是,調用TDerived構造函數作爲TBase::Call(...)的第三個參數的默認參數沒有任何問題。有人能給我一個關於發生了什麼的明確答案嗎?爲什麼派生類MFP不被接受,並且派生類的對象被接受爲默認參數?

謝謝。

編輯:編譯器的錯誤(MSVS2010命令行編譯):

FMain.cpp(224) : error C2061: syntax error : identifier 'TDerived_'; FMain.cpp(233) : see reference to class template instantiation 'TBase<PValue,PDerived> with [PValue=int,PDerived=TDerived<int>]' being compiled; FMain.cpp(323) : see reference to class template instantiation 'TDerived<PValue> with [PValue=int]' being compiled 

這是一個語法錯誤 - 它不承認TDerived_如在MFP的默認參數類型。在這之後還有其他錯誤,它們都是語法錯誤,因爲函數定義現在是不合格的。這就是我的理解。

編輯:基本上,我不明白爲什麼我可以使用TDerived_作爲默認參數的對象,但不能使用指向成員函數的指針作爲默認參數。

編輯:好吧,這現在讓我瘋了。 首先,我改爲typedef TBase< PValue, TDerived > TBase_;,因爲它被指出(謝謝,夥計們!)。實際上,它只在MSVC++下編譯,因爲此編譯器不執行兩部分解析;即在codepad.org上(它使用g ++ 4.1.2),它沒有編譯。 其次,在那之後,我嘗試在codepad.org上使用static void Call(PType /*pSomething*/, void(TDerived_::*pFunction)(void) = &TDerived_::Foo, TDerived_ pDerived = TDerived_()),並且......編譯並正確運行!所以我現在真的很困惑:人們向我解釋爲什麼它不正確(我不明白「爲什麼」(參見我之前的編輯)),現在事實證明g ++編譯它是正確的......這是否意味着它只是MSVC++的問題,而不是代碼?或者,從標準的角度來看,代碼確實存在問題(我看不到它),並且g ++「錯誤地」接受它(不太可能,我想)?幫助?!

+0

編譯器給出了什麼錯誤?你爲什麼不發佈這個呢?是否希望我們編譯此代碼並自己查看錯誤? – Nawaz

+0

「語法錯誤」錯誤消息是可能的最不有用的診斷消息(除「某處出現錯誤」)。 – curiousguy

+0

回覆:您最近的評論 - 在typedef更改後,代碼對我來說很合適,但也許對標準有更廣泛認識的人可以發表評論。 FWIW,在VS2008和GCC 4.2.1上編譯並運行後, – msandiford

回答

2

TValue_參數在TDerived中的typedef TBase_類型的作用域似乎是錯誤的

你有(!):

private: 
    typedef TBase< TValue_, TDerived > TBase_; 

我想你需要:

private: 
    typedef TBase< typename TBase< PValue, TDerived<PValue> >::TValue_, TDerived > TBase_; 

甚至只是:

private: 
    typedef TBase< PValue, TDerived > TBase_; 

編輯:C++標準部分14.6。2第3覆蓋這種情況下:

在一類或類模板的定義中,如果一個基類 取決於模板參數,基類範圍並不或者在非限定名稱查找期間檢查 實例TBase< int, TDerived< int> >模板類定義Call<>功能TE的聲明時:的 類模板或構件或定義的點的類 模板或構件

+0

'TValue_'在'TBase'中定義爲受保護的'typedef'。 「TDerived」從「TBase」公開繼承並可以訪問其受保護的定義。 – lapk

+0

@AzzA實際上msandiford並沒有說「TValue_'在這種情況下無法訪問,他說它沒有正確的」範圍「。他的意思是'TValue_' **在這裏不可見**。 – curiousguy

+0

有關爲什麼'TValue_' **不能**在C++ **的派生類**中可見的解釋,請參閱[在析構函數中使用此關鍵字(關閉)](http://stackoverflow.com/questions/7908248/using-this-keyword-in-destructor/7908530#7908530) – curiousguy

1

簡單的實例化期間mplate被實例化:

template< typename PType > 
    static void Call(PType , void(TDerived_::*pFunction)() = &TSelf_::Foo, TDerived_ pDerived = TDerived_()) 

(與TDerived_ = TDerived< int>),如TSelf_::Foo()在該點聲明這是好的。

OTOH,與

static void Call(PType , void(TDerived_::*pFunction)() = &TDerived_::Foo, TDerived_ pDerived = TDerived_()) 

問題是,TDerived_::Foo()沒有在TBase< int, TDerived< int> >模板類的定義實例聲明。

順便說一句,你不需要指定參數列表(void); ()具有相同的效果並且不太冗長。

+0

是的,但第三個默認參數呢?爲什麼我可以使用'TDerived_'作爲第三個默認參數?如果我不能使用'TDerived_'成員函數的地址,我如何在同一個作用域中創建一個'TDerived_'對象?我一定很愚蠢,但我仍然不明白... – lapk

+0

我想,我需要得到一個遵循標準的編譯器。當編譯器從根本上允許不按照標準編譯的代碼時,它可能毫無意義地詢問「爲什麼編譯和不編譯」。感謝您的投入,夥計們。 – lapk

+0

@AzzA「_爲什麼我可以使用'TDerived_'臨時作爲第三個默認參數?_」知道'TDerived_'是一個類型,'TDerived _()'的含義是已知的:構造一個類型爲TDerived_'的臨時對象。當然,只有一個構造函數可以接受0個參數,並且可以在此上下文中訪問,但必須稍後再進行檢查**,但這不會影響表達式是右值鍵入'TDerived_'。 OTOH編譯'TDerived _ :: Foo',編譯器需要能夠查找名稱以確定表達式的類型。 – curiousguy