2012-11-26 125 views
3

我一直在解決至少兩週的問題,被我無法理解的東西阻塞,並且在SO中提出的問題並未真正指向真正的問題(愚蠢的我!)。最後,現在,我希望在這個問題上我發現了我最頭疼的一點。MSVC指向模板參數中重載函數的指針

我一直在使用的模板結構作爲輔助檢測一個給定的類型是否有一個成員方法還是不行,這個模板結構是這樣的:

template 
< 
    typename Type, 
    typename Return, 
    typename Parameter, 
    Return (Type::*)(Parameter) 
> struct W {}; 

角落找尋struct W的主要思想是把作爲第四個參數的成員函數指針指向我需要使用SFINAE技巧測試的成員函數in a previous question提出了一個替代方案,它幫助我理解了許多我一直想念的概念,但最終它並不能解決我真正的問題。

我正在使用的那段代碼必須在Linux和Windows平臺上工作,我用於windows的編譯器是MSVC(Visual Stuido 2010 10.0)和gcc(Devian 4.4.5-8)在Linux方面。問題是,同一段代碼不能在MSVC中編譯,但它在gcc中(我很震驚,通常是相反的,因爲MSVC不太標準)。

問題的起源是我的SFINAE方法無法檢測方法set::insert而MSVC下編譯,爲了尋找這個失敗,我寫了一個simple test

#include <set> 

int main(int argc, char **argv) 
{ 
    typedef std::set<int> setint; 

    std::pair<setint::iterator, bool> (setint::*p_1)(const setint::value_type &) = &setint::insert; 
    W<setint, std::pair<setint::iterator, bool>, const setint::value_type &, &setint::insert> w_1; 

    return 0; 
} 

就像我之前提到:這使用gcc編譯樣本沒有問題,但在使用MSVC在w_1聲明給出了一個錯誤:

error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'std::pair<_Ty1,_Ty2> (__thiscall std::set<_Kty>::*)(const int &)' 
    with 
    [ 
     _Ty1=std::_Tree_const_iterator<std::_Tree_val<std::_Tset_traits<int,std::less<int>,std::allocator<int>,false>>>, 
     _Ty2=bool, 
     _Kty=int 
    ] 
    None of the functions with this name in scope match the target type 

在創建函數指針正常工作,所以聲明編譯;但與模板參數相同的簽名沒有。這就是在SFINAE的符號替換過程中set::insert檢測失敗的原因。錯誤文本cannot convert from 'overloaded-function'沒有提供任何有關正在發生的事情的線索(或者我無法找到任何,因爲我的英語不好)。乍一看,我一直認爲問題是符號替換,但如果MSVC和gcc都失敗了,它也會有意義。所以現在我想知道問題是否是某種MSVC編譯器特定的方式來做事情。

任何線索爲什麼它在MSVC下失敗,以及如何使gcc和MSVC以同樣的方式工作?

額外的問題:std::mapstd::set提供了嵌套式_Pairib作爲返回類型::insert方法MSVC實現紅黑樹(或一切mapset下)下,有一個在GCC實現不相同呢?

編輯:

閱讀chill answer之後,我進去看了MSVC的std::set實現。

std::set是派生類的std::_Tree提供我想要查詢的方法,包括:

// class std::_Tree, file xtree in the path 'VisualStudioPath/VC/include/' 
_Pairib insert(const value_type& _Val) 
    { // try to insert node with value _Val, favoring right side 
    return (insert(_Val, false)); 
    } 

該插入方法屬於std::_Tree範圍,而不是std::set範圍,但它是到公共範圍和是一個繼承的方法,所以爲什麼它在名稱替換期間不可訪問?

+1

關於額外的問題:'_Pairib'保留用於執行的名稱,用戶代碼不應該參考它。保留標識符的集合是:標識包含雙下劃線('__'),或以任何名稱空間中的單個下劃線和大寫字母('_P..')開頭,或單個下劃線後面跟全局名稱空間中的任何字符。 –

回答

2

未能原因是,在VC STDLIB,該insert實際上不是set類模板本身的一員,但繼承:

測試用例,

template 
< 
    typename Type, 
    typename Return, 
    typename Parameter, 
    Return (Type::*)(Parameter) 
> struct W {}; 

struct A { void foo (int); }; 

struct B : public A {}; 

int main(int argc, char **argv) 
{ 
    void (B::*p)(int) = &B::foo; 
    // W<B, void, int, &B::foo> w; // error 
    W<A, void, int, &A::foo> w; 

    return 0; 
} 

PS。這個錯誤在一些GCC和一些MSVC中都是錯誤的。

但它是在公共範圍內,也是一種繼承方法,所以我不明白爲什麼它不可訪問。

因爲沒有轉換是允許在這種情況下,「14.3.2。模板非類型參數[#5]」

對於類型指針的非類型模板參數成員函數, 如果模板參數的類型爲std :: nullptr_t,則應用指針轉換(4.11)的空成員 ; 否則,不適用。 如果template-argument代表一組過載的成員 函數,則從集合 (13.4)中選擇匹配成員函數。

PPS。所以,你做的轉換明確,瞧,你不需要關心可能的基類:

W<setint, 
    std::pair<setint::iterator, bool>, 
    const setint::value_type &, 
    static_cast<std::pair<setint::iterator, bool> (setint::*)(const setint::value_type &)> (&setint::insert)> w_1; 
+0

我認爲問題與繼承無關......我會檢查它。 –

+0

'_Pairib插入(const value_type&)'的聲明和實現在'class _Tree'中,而不在'class set'中,你是對的。但它在公共範圍內,也是一種繼承方法,所以我不明白爲什麼它不可訪問。 –

+0

我已經用幾個月的解決方法解決了這個問題,這個答案並不能解決我的具體問題,但是確實值得閱讀,因爲它可以指出某人在解決方案中遇到同樣的問題。 –

0

根據this問題,標準沒有定義您可以將成員函數指針帶到標準對象的成員。

_Pairib是一個MSVC實現細節 - 當然你不會在GCC中找到類似的東西。