2014-01-20 14 views
6

我知道模板成員函數僅在使用時纔會生成。如果不是所有使用的類型都支持這種功能,這很方便。但是,對於具有尾隨返回類型規範的函數,這似乎不起作用。下面是一個小實驗:具有結尾返回類型的模板成員函數,即使未使用也會出錯

// helper function for case A workaround 
template <typename A, typename T> 
auto F(T&& x) 
-> decltype(x.template f <A>()) 
    { return x.template f <A>(); } 

// helper function for case B workaround 
template <typename A, typename T> 
auto G(T&& x) 
-> decltype(x.g()) 
    { return x.g(); } 

template <typename T> 
struct S 
{ 
    // case A: not ok in GCC + Clang 
    template <typename A> 
    auto f1() 
    -> decltype(T().template f <A>()) 
     { return T().template f <A>(); } 

    // case A workaround: ok in Clang + GCC 
    template <typename A> 
    auto f2() 
    -> decltype(F <A>(T())) 
     { return F <A>(T()); } 

    // case B: ok in GCC, not ok in Clang 
    template <typename A> 
    auto g1() 
    -> decltype(T().g()) 
     { return T().g(); } 

    // case B workaround: ok in GCC + Clang 
    template <typename A> 
    auto g2() 
    -> decltype(G <A>(T())) 
     { return G <A>(T()); } 
}; 

請記住這個樣本只是爲了說明這個問題,它不是別的有用的。

S <T>可以被實例化爲具有合適的成員函數f,g的任何類型T

但是,如果我嘗試實例化S <int>,例如,通過S <int> s{};,我確實收到錯誤,如type 'int' is not a structure or union。這種情況發生在f1,g1這兩種情況下,它們試圖分別在T(在這種情況下爲int)的值上調用模板函數f或非模板函數g。即使我不試圖在對象s上撥打f1g1,也會發生這種情況。但是,GCC在g1的情況下罰款;鏗鏘不是。

對於案例A(模板成員函數f)的解決方法是使用幫助器函數F,這是f2所做的,並且對於Clang和GCC都可以正常工作。它似乎工作,因爲調用T().template f <A>()對於f2的聲明是隱藏的,編譯器在A類型未知時未查找到F <A>(T())

案例B(非模板成員函數g)的相同解決方法也適用於這兩種編譯器。

我希望能幫助您找出發生的一切。哪種情況下的正確行爲?哪個編譯器是正確的?總體來說還有其他解決方法嗎?

我正在使用GCC 4.8.1和Clang 3.3。

+0
+1

@Gasim它可能很醜,但不幸的是需要'template'關鍵字。例如'f'可能是一個數據成員,所以'x.f iavr

+0

你知道['std :: enable_if'](http://en.cppreference.com/w/cpp/types/enable_if)嗎? – Constructor

回答

1

SFINAE僅適用於函數的模板參數,而不適用於從類繼承的模板參數。

不同的解決方案是包括複製噸至第二個模板參數,但是這只不過是一個較短版本的解決方法的更多:

#include <utility> 
#include <type_traits>  
struct Foo { 
    template < typename T > T f() { return {}; } 
}; 
template <typename T> 
struct S { 
    template <typename A, typename TT = T > 
    auto f1() -> decltype(std::declval<TT>().template f <A>()) { 
     static_assert(std::is_same<T,TT>::value, "TT must be equal to T"); 
     return TT().template f <A>(); 
    } 
}; 

int main() { 
    S<Foo> a; 
    a.f1<int>(); // ok 

    S<int> b; 
    b.f1<int>(); // not ok 
} 
+0

謝謝,這個解決方法肯定是更好的,我會記住(目前,我已經繞過這個問題完全通過使用額外的功能,使代碼更通用無論如何)。我使用了一個類似的想法來引入一個虛擬模板參數,通過enable_if控制一個非模板默認構造函數(默認構造函數沒有參數並且沒有返回類型,所以沒有地方放置enable_if,除非它變成模板)。 – iavr

+0

@iavr上面的結果導致一個不需要診斷的惡意程序:所有'template'函數都必須至少有一個有效的專業化。 – Yakk

相關問題