2012-01-21 24 views
5

我們有複雜的模板類,它們有一些不適用於特定策略或類型的方法。因此,當我們檢測到這些類型(編譯時,使用類型特徵)時,我們會發出一條帶有好消息的靜態斷言。我可以從手動模板實例化中排除一些方法嗎?

現在我們也做了大量的手動模板實例化。部分原因是這些方法被強制編譯爲語法檢查方法。它還減少了圖書館用戶的編譯時間。問題是靜態斷言總是被觸發,因此我們不能手動實例化模板類。

是否有解決方法?

編輯:使其更清晰,這裏有一個例子(在這種情況下,顯式實例都將失敗someFunc1():

// header 
template <typename T> 
class someClass 
{ 
    void someFunc() {} 
    void someFunc1() { static_assert(false, assertion_failed); } 
}; 

// source 
template someClass<int>; // Explicit instantiation 

EDIT2:這裏是另一個例子這個時候你可以編譯它明白我的意思。首先馬上編譯,代碼應編譯,然後取消註釋[2]和靜態斷言應該解僱。現在註釋掉[2]取消註釋[1]。該靜態斷言無線會因爲你明確地實例化模板而着火。我想避免刪除明確的實例化,因爲它帶來的好處(請參閱上面的好處)。

namespace Loki 
{ 
    template<int> struct CompileTimeError; 
    template<> struct CompileTimeError<true> {}; 
} 

#define LOKI_STATIC_CHECK(expr, msg) \ 
    { Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

template <typename T> 
class foo 
{ 
public: 

    void func() {} 
    void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); } 
}; 

template foo<int>; 
//template foo<double>; // [1] 

int main() 
{ 
    foo<int> a; 
    a.func1(); 

    foo<double> b; 
    //b.func1(); //[2] 

    return 0; 
} 
+0

從描述中不清楚問題是什麼,但從問題的範圍來看,它似乎是enable_if是你的(http://www.boost.org/doc/libs/1_48_0/libs/utility/ enable_if.html) – bobah

+0

@bobah:這根本不是一個壞主意......我會研究它。如果您可以準備一個我可以驗證的簡單示例,我會將其標記爲答案? – Samaursa

+1

我同意那些抱怨缺乏細節的人。但無論如何,這給我敲響了一個鐘聲:「問題是靜態斷言總是被解僱。」 http://www.boost.org/doc/libs/1_48_0/doc/html/boost_staticassert.html#boost_staticassert.templates,最後一句話:你遇到的問題是什麼? –

回答

0

通過bobah的建議,但我沒有拿出一個解決方案,不需要升壓和我沒有得到一個機會來測試enable_if滿足我的一個很好的程度原來的要求(我說而不是,將在年底講解)

解決的辦法是把虛擬模板上的代碼,如果在某些選定的編譯,將失敗類型和是在別人下罰款。所以:

struct dummyStruct {}; 

#define DUMMY_TEMP typename dummy 
#define DUMMY_PARAM dummyStruct 

namespace Loki 
{ 
    template<int> struct CompileTimeError; 
    template<> struct CompileTimeError<true> {}; 
} 

#define LOKI_STATIC_CHECK(expr, msg) \ 
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

template <typename T> 
class foo 
{ 
public: 

    void func() {} 
    template <typename T_Dummy> 
    void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); } 
}; 

template foo<int>; 
template foo<double>; // [1] 

int main() 
{ 
    foo<int> a; 
    a.func1<DUMMY_PARAM>(); 

    foo<double> b; 
    //b.func1<DUMMY_PARAM>(); //[2] - this is a static error 

    return 0; 
} 

在我所有的模板代碼,這幾樣功能(即具有靜態的那些斷言或某些類型的工作,並可以通過使用類型特徵[在這種情況下,有選擇失敗他人對不同類型的幾種不同功能])對客戶端是隱藏的。所以在我的實施中,增加額外的dummy parameter是一個不錯的妥協。

作爲獎勵,它讓我知道這個功能是專爲只有某些類型使用。此外,我最初的顯式實例化問題可以通過這種簡單的技術來解決。

2

好了,所以如果你強迫使用顯式實例的所有方法的實例,你不能逃脫任何編譯時的技巧,以防止有問題的方法實例,如enable_if。將錯誤移到運行時會很容易,但這是不可取的。

我認爲你可以做的最好的事情就是將錯誤移動到鏈接時間,這會靜態地確保程序不包含可能會調用被禁函數的代碼路徑,但錯誤消息不會很有幫助給任何不知道你施加的限制的人。總之,解決的辦法是宣佈的禁止成員函數的專業化而不是將它們定義:

template<typename T> 
struct Foo { 
    void bar() { 
     std::cout << "bar\n"; 
    } 
    void baz() { 
     std:: cout << "baz\n"; 
    } 
}; 

template<> void Foo<int>::baz(); // use of Foo<int>::baz() will resolve to this specialization, and linking will fail 

template struct Foo<int>; 
template struct Foo<char>; 

int main() { 
    Foo<int> f; 
    f.bar(); 
    // f.baz(); // uncommenting this line results in an ugly link time error 
    Foo<char> b; 
    b.bar(); 
    b.baz(); // works with Foo<char> 
} 

靜態斷言不再幫助給友好的錯誤消息,當一個錯誤在客戶端代碼做,但你可能要如果你忘記提供專業化,他們會因爲他們會開火而離開他們。

+1

如果你正在創建一個實例,那很好。在我們的例子中,我們使用明確的實例化來解決問題(參見上面的編輯) – Samaursa

+0

爲了避免任何未來的SOer混淆,我上面的評論是針對編輯的回覆_before_(這是在我通過一些編輯澄清我的問題之前做出的) ) – Samaursa

+0

+1 **這應該是答案**需要一點多餘的工作,但提供:) – CodeAngry

1

enable_if是精確模板方法定位的靈活機制,可能就是您所追求的。例如:

#include <string> 
#include <iostream> 

#include <boost/utility.hpp> 
#include <boost/type_traits.hpp> 
#include <boost/static_assert.hpp> 

template <class T> class mywrapper 
{ 
    T _value; 

    template <class V> 
    typename boost::enable_if<boost::is_scalar<V>, void>::type printval_(V const& value) 
    { 
    BOOST_STATIC_ASSERT(boost::is_scalar<V>::value); 
    std::cout << "scalar: " << value << std::endl; 
    } 

    template <class V> 
    typename boost::enable_if<boost::is_compound<V>, void>::type printval_(V const& value) 
    { 
    BOOST_STATIC_ASSERT(boost::is_compound<V>::value); 
    std::cout << "compound: " << value << std::endl; 
    } 

public: 
    mywrapper(T const& value):_value(value) { } 
    void printval() { printval_(_value); } 
}; 

template class mywrapper<int>; 
template class mywrapper<std::string>; 

int main() 
{ 
    mywrapper<int> ival(333); 
    mywrapper<std::string> sval("test"); 

    ival.printval(); 
    sval.printval(); 
    return 0; 
} 
+0

我目前沒有'boost',但是這樣做是否還會顯式實例化?例如,在'int main()'之前,如果你把'te​​mplate mywrapper ; template mywrapper ;'並且在每個函數中,您都有一個靜態斷言來確保您分別使用標量和複合類型;它會編譯嗎? – Samaursa

+0

用靜態斷言和實例更新了示例 – bobah

+0

感謝bobah!我會盡快進行測試。 – Samaursa

3

不能兼得:你不能有一個靜態斷言,以防止實例顯式實例的類型!這是一個明顯的矛盾。然而,你可以擁有的是有條件的包含功能,即使它在某種程度上是一種痛苦:如果某個成員函數不應該被支持某些類型,你可以將這個函數移動到一個有條件的基類中。這樣你就不會使用靜態斷言,而只是刪除成員函數。我意識到這會引入一些有趣的其他問題,例如關於成員變量的位置,但我認爲在上下文中你描述的是你能得到的最好的。

下面是如何可能看起來像一個簡單的例子:

template <typename T, bool = std::numeric_limits<T>::is_integer> struct foo_base; 
template <typename T> struct foo_base<T, false> { /* intentionally left blank */ }; 
template <typename T> struct foo_base<T, true> { void foo() { /*...*/ } }; 

template <typename T> 
struct Foo: foo_base<T> { /* .... */ }; 

template struct Foo<int>; // will have foo() 
template struct Foo<double>; // will not have foo() 
相關問題