2010-11-09 262 views
4

使用GCC編譯我從下面的代碼中總是得到false。我相信這是一個編譯器錯誤,但有人可能會更清楚。爲什麼下面的SFINAE測試不能檢測模板成員函數?

#include <iostream> 


template< class T > 
class has_apply { 

    typedef char yes[1]; 
    typedef char no[2]; 

    template< class U, U u > 
    struct binder {}; 

    template< class U, unsigned n > 
    static yes& test(U*, 
         binder< void (U::*) (const double&), 
          &U::template apply<n> 
          >* = 0 
       ); 

    template< class U, unsigned n > 
    static no& test(...); 

public: 

    static const bool result = 
     (sizeof(yes) == sizeof(test< T, 0u >((T*)(0)))); 

}; 

class A { 
public: 
    template< unsigned n > 
    void apply(const double&); 

}; 

int main() 
{ 
    std::cout << std::boolalpha << has_apply<A>::result << '\n'; 
    return(0); 
} 
+2

你有可能簡化代碼嗎? (我敢肯定,你不需要所有7個'yes'模板來證明問題。)或者至少添加一些評論? – 2010-11-09 17:08:37

+0

我已經添加了6個測試來涵蓋所有可能性,但奧利查爾斯沃思留下的測試應該足以產生「真實」。 – Elias 2010-11-09 17:48:08

+0

是的,我冒險只有這一個是相關的。請隨時編輯適當的! – 2010-11-09 17:51:48

回答

1

我不能說明白爲什麼,但我可以不採取U *,並通過拉動粘合劑的聲明類型出來,讓您的工作代碼:

template< class T > 
class has_apply { 

public: 
    typedef char yes[1]; 
    typedef char no[2]; 

    template< class U, U u > 
    struct binder {}; 
    typedef binder< void (T::*)(const double&), &T::template apply<0u> > b; 

    template < typename V, unsigned n > 
    struct declare 
    { 
    typedef binder< void (V::*)(const double&), &V::template apply<n> > type; 
    }; 

    template< typename U, unsigned n > 
    static yes& test(typename declare<U,n>::type *); 

    template< class U, unsigned n > 
    static no& test(...); 


    static const bool result = 
     (sizeof(yes) == sizeof(test< T, 0u >(0))); 

}; 

你可以實際上通過從函數中刪除unsigned參數來簡化這一點,並在'declare'中的typedef中加上0u。

同樣,我無法解釋爲什麼這中間元函數是必要的,但它需要在MSVC++ 2010

+1

+1適用於MSVC的東西。我也試過用g ++ 4.4.1,但是g ++說「x.cpp:在靜態成員函數中'static char(&has_apply :: test(typename has_apply :: declare :: type *))[1]' : x.cpp:21:錯誤:'yep'未在此範圍內聲明「。所以,不完整 - 我不明白編譯器的行爲。乾杯, – 2010-11-09 19:21:45

+0

哦,哎呀。您必須刪除yes測試的正文。我會編輯來做到這一點。我用來計算WTF的部分代碼。 – 2010-11-09 19:23:41

+0

更新:我添加了'yep'的定義,然後編譯MSVC和g ++。但是後來我增加了額外的arg來'apply'來檢查'false'結果,然後它不用任何一個編譯器編譯。 :-(你有更多的魔力嗎?乾杯, – 2010-11-09 19:25:33

0

上述作品挪亞一樣,我不知道爲什麼。與Noah不同,我沒有找到可行的解決方案,但調查了我設法使MingW g ++ 4.4.1編譯器崩潰的事件(即內部編譯器錯誤)。這是簡單地通過不一致參照apply爲模板和非模板:上

#include <iostream> 


template< class T > 
class has_apply { 
    template< class U, U u > 
    struct binder {}; 

    template< class U > 
    static double test(
    U*, 
    binder< 
     void (U::*) (const double&), 
     //&U::template apply<0> 
     &U::apply 
     >* = 0 
); 

public: 

    static binder< 
     void (T::*) (const double&), 
     &T::template apply<0> 
     >* dummy(); 

    static const bool result = sizeof(test((T*)(0), dummy())); 
}; 

class A { 
public: 
// template< unsigned n > 
    void apply(const double&); 

}; 

int main() 
{ 
    std::cout << std::boolalpha << has_apply<A>::result << '\n'; 
    return(0); 
} 

影響G ++:

 
C:\test> g++ -std=c++98 y.cpp 
y.cpp: In instantiation of 'has_apply': 
y.cpp:38: instantiated from here 
y.cpp:24: internal compiler error: in instantiate_type, at cp/class.c:6303 
Please submit a full bug report, 
with preprocessed source if appropriate. 
See for instructions. 

C:\test> _ 

他他......

PS:我很樂意發佈這是一個「評論」,因爲它不是一個「答案」。 [comp.lang.C++主持]

1

安迪Venikov的回答了在(我只考慮信用偉大的谷歌 - 富(他他,我騙)):

http://groups.google.com/group/comp.lang.c++.moderated/msg/93017cf706e08c9e

+0

這並不完全回答爲什麼它不起作用的問題 請注意,那裏的傢伙也不知道爲什麼結果不是我期望的結果 – Elias 2010-11-13 15:51:05

+0

@Elias:我沒有完全理解Andy的答案(因此,只能鏈接到它),但他暗示了常量表達上下文。我能找到的最接近的是C++ 98§5.19/ 4「一個表達式,指定成員的地址或非POD類對象的基類(第9章)不是地址常量表達式「但我看不出爲什麼成員函數模板應該使'A'類成爲非POD,當我檢查這個時,正如我記得(適當修改)的代碼非模板成員很好地工作。好的,這可能不會直接幫助你。只是又一個數據點......乾杯, – 2010-11-13 16:10:37

+0

如果你現在看看這個話題,你會發現它已經認識到安迪在他的第一篇文章中的推理其實是錯誤的。在此之前,我寧願保持沉默,但也不確信他的答案。 他提供的鏈接很有價值。 – Elias 2010-11-13 19:07:56

0

這是不是爲什麼它不起作用的答案。然而,通過網絡進行研究,我發現了一些例子,並最終得到了下面的代碼,這可能會讓我更加努力。

我試圖檢測特定的成員函數簽名,但下面的代碼超越並檢測給定的調用是否可能,無論簽名是什麼。希望評論會有所幫助。

#include <iostream> 

template< class T > 
class has_apply { 

    class yes { char c; }; 
    class no { yes c[2]; }; 

    struct mixin { 
void apply(void); 
    }; 

    // Calling derived::apply is only non-ambiguous if 
    // T::apply does not exist, cf. 10.2.2. 
    template< class U> struct derived : public U, public mixin {}; 

    // The following template will help on deduction based on this fact. 
    // If U is type void (mixin::*) (void) then the template can be 
    // instantiated with u = &derived<U>::apply if and only if T::apply 
    // does not exist. 
    template< class U, U u > 
    class binder {}; 

    // Therefore, the following template function is only selected if there 
    // is no T::apply: 
    template< class U > 
    static no deduce(U, binder< void (mixin::*) (void), &derived<U>::apply >* = 0); 
    // Selected otherwise. 
    static yes deduce(...); 

    // Provides an T object: 
    static T T_obj(void); 

public: 

    static const bool result = (sizeof(yes) == sizeof(deduce(T_obj()))); 

}; 

namespace aux { 

// Class to represent the void type as a "true" type. 
class void_type {}; 

// deduce() some lines below will give us the right answer based on 
// the return type of T::apply<>, but if it is void we cannot use a 
// call to T::apply as an argument to deduce. In fact, the only 
// function in c++ that can take such an argument is operator,() with 
// its default behaviour and if an overload is not well formed it 
// falls back to default. 
template< class T > 
T& operator,(const T&, void_type) {}; 

// Copies the constness of T into U. This will be required in order 
// to not get false positives when no const member is defined. 
template< class T, class U > 
struct copy_constness { 
    typedef U result; 
}; 
template< class T, class U > 
struct copy_constness< const T, U > { 
    typedef const U result; 
}; 
} 

template< class T > 
class has_correct_apply{ 

    class yes { char c; }; 
    class no { yes c[2]; }; 

    // We assume has_apply<T>::result is true so the following class 
    // is well declared. It is declared in a way such that a call to 
    // derived::apply<n>() is always possible. This will be necessary 
    // later. 
    struct derived : public T { 
using T::apply; // possible iff has_apply<T>::result == true 
// This template function will be selected if the function call 
// we wish is otherwise invalid. 
template< unsigned n > 
static no apply(...); 
    }; 

    // const_correct_derived will have the same constness than T. 
    typedef typename aux::copy_constness< T, derived >::result const_correct_derived; 
    // Provides a const correct derived object. 
    static const_correct_derived derived_obj(void); 

    // Only possible call was derived::apply: call is impossible for signature: 
    static no deduce(no); 
    // Since te returned value of it will most likely be 
    // ignored in our code (void must be always [almost, see next] 
    // ignored anyway), we return yes from this: 
    static yes deduce(...); 
    // As we noticed, an overload of operator,() may make an exact match necessary. 
    // If we want this we could simply have used "no" instead of "yes" above and: 
// static no deduce(aux::void_type); 


public: 

    static const bool result = (sizeof(yes) == sizeof(deduce(
(derived_obj().template apply<0u>(0.0), aux::void_type()) 
))); 

    // Note: Inteestingly enough, GCC does not detect an private subclass default 
    // constructor and so const_correct_derived() could be used instead of 
    // having a function derived_obj(), but I do not know if this behavoiur is 
    // standard or not. 

}; 


struct C { 
    template< unsigned n > 
    int apply(double, unsigned m = 10) const; 
private: 
    C(); 
}; 

struct D { 
    template< unsigned n > 
    int apply(const double&); 
private: 
    D(); 
}; 

struct E : public C { 
}; 

struct Without{}; 

#include "mp.h" 

int main() 
{ 
    std::cout << has_apply<E>::result << '\n'; 
    std::cout << has_correct_apply< const E >::result << '\n'; 
    std::cout << has_correct_apply< const D >::result << '\n'; 
    std::cout << has_correct_apply<D>::result << '\n'; 
// E e; 

    return(0); 
} 
相關問題