2011-02-27 178 views
6

我通常會聲明我的類和模板,然後在(在當前的頭文件中)定義它們的方法。我發現這樣更容易閱讀。那麼,我遇到了一個案例,我無法弄清楚一個工作類型簽名是否在一個不符合類的定義中使用。下面是我在做什麼一個簡單的例子,說明該問題:類型的enable_if'ed模板模板構造函數的簽名?

template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template 
     < class Iterator 
     , enable_if< is_iterator<Iterator> > 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ ... } 

template <class T> 
template 
    < class U 
    , WHAT_GOES_HERE? 
    > 
Foo<T>::Foo(U f, U l) 
{ ... } 

我已經嘗試了一些在WHAT_GOES_HERE插槽的東西,試圖得到一個匹配的簽名,我一直失敗。我需要使用enable_if來區分兩個T類型對象傳入的情況,以及一個傳入一對Iterator時的情況。如果模板化的構造函數是在主模板內部定義的,那麼代碼就可以正常工作,而代碼目前是這樣做的,但我寧願將該定義移動到聲明之外。

編輯:我要指出,我不能只重複使用enable_if < ...>的定義,因爲enable_if < ...>指定其類型,你不能做一個缺省值一個定義也不是一個聲明。

+1

你真的需要SFINAE這個?如果您只聲明第二個構造函數爲'template Foo(U first,U last);',如果調用者傳遞兩個類型爲T的對象,則仍然會選擇第一個構造函數。 – 2011-02-27 20:21:47

+0

類型T通常是一種算術類型,我希望在T無符號時能夠傳入整數,反之亦然,並且沒有調用模板構造函數(在使用enable_if之前發生了這種情況) – swestrup 2011-02-27 20:29:13

+0

實際上,你根本沒有分配一個默認值。你的模板的第二個參數是'enable_if ''。有點像如果你期望一個'int'。它不應該編譯,當然也不可能使用。 – 2011-02-27 20:33:42

回答

3

我不會這麼做。這裏的變化我會做:

template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template 
     < class Iterator 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
     , typename enable_if<is_iterator<Iterator> >::type* = 0 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ ... } 

template <class T> 
template 
    < class U 
    > 
Foo<T>::Foo(U f, U l, typename enable_if< is_iterator<U> >::type*) 
{ ... } 

這是直出的enable_if的文件。

+0

有趣的是,我已經多次閱讀了enable_if文檔,並且我從來沒有想到將構造函數作爲嵌套函數處理......如果這有效,那麼它肯定是可以接受的。 – swestrup 2011-02-27 20:35:12

+0

請參閱http://www.boost.org/doc/libs/1_46_0/libs/utility/enable_if。html第3節:「構造函數和析構函數沒有返回類型;只有一個額外的參數。」 – 2011-02-27 20:38:40

+0

謝謝!那的確有訣竅。這是否意味着我之前嘗試的是不可能的,因爲沒有辦法給出滿足C++的類型簽名? – swestrup 2011-02-27 20:46:44

2

這是你想要完成的嗎? [我沒有is_iterator類型特徵,因此我使用C++ 0x類型特徵和實用程序庫重新編寫了您的示例。它應該以與TR1和Boost庫相同的方式工作。]

#include <utility> 
#include <type_traits> 

template <typename T> 
struct S 
{ 
    // Constructor (1) 
    S(T, T); 

    // Constructor (2) 
    template <typename U> 
    S(U, U, typename std::enable_if<std::is_integral<U>::value>::type* = 0); 
}; 

template <typename T> 
S<T>::S(T, T) 
{ } 

template <typename T> 
template <typename U> 
S<T>::S(U, U, typename std::enable_if<std::is_integral<U>::value>::type*) 
{ } 

int main() 
{ 
    S<double> a(1.0, 2.0); // uses (1) 
    S<double> b(1, 2);  // uses (2) 
} 
+0

是的,這或多或少是正確的。我必須寫我自己的is_iterator。我不知道爲什麼它不是標準的提升。 – swestrup 2011-02-28 22:19:31

1

,你可以做最簡單的是:

template<class Iterator> 
Foo 
    (Iterator first 
    , typename enable_if<is_iterator<Iterator>, Iterator>::type last 
); 
1
template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template <class Iterator 
     ,  class = typename std::enable_if 
         <is_iterator<Iterator>::value> 
         ::type 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ } 

template <class T> 
template 
    < class U 
    , class > 
Foo<T>::Foo(U f, U l) 
{ } 
+0

我試過這個,它不起作用。我收到一條消息,說該定義與我的模板中的任何聲明不匹配。雖然,我必須說,這**應該**工作! – swestrup 2011-02-27 20:55:18

+0

我用g ++ - 4.4和clang測試了它。不過,我的測試中有-std = C++ 0x,現在我仔細檢查了一下,我認爲這是必需的。如果沒有它,clang會給出警告:警告:函數模板的默認模板參數是C++ 0x擴展名[-WC++ 0x擴展名] ,class = typename std :: enable_if – 2011-02-27 21:13:25

+0

奇怪的是,您的示例可以工作,我用我的編譯器嘗試它。但是,當我在真實模板上嘗試相同的東西時,出現錯誤。我一定在做錯事... – swestrup 2011-02-28 22:34:40