2012-07-12 44 views
2

在我的模板中,我需要根據typename是否是基本類型來使用不同的代碼部分。C++:Use #if std :: is_fundamental <T> :: MSVC 2010中條件編譯的值

編譯此代碼給出MSVC一個C4067(以下預處理指令意外令牌 - 預計一換行):

template <typename T> 
void MyClass<T>::foo() 
{ 
// ... some code here 
#if std::is_fundamental<T>::value 
    if(m_buf[j] < m_buf[idx_min]) 
     idx_min = j; 
#else 
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]); 
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]); 
    // error checking removed for simplicity 
    if(curr.before(curr_min)) 
     idx_min = j; 
} 
#endif 

模板是既原始和我自己的(自ASSortable派生)工作的數據類型和錯誤從模板實例代碼拋出:

template class MyClass<char>; 

試圖修改預編譯表達這個沒有工作之一:

#if std::is_fundamental<T>::value == true 

併產生相同的確切警告。

任何想法如何使此代碼免受警告?

編輯我想到的另一件事是將其轉換成一個運行時檢查,並與「不變,如果表達式」警告住......難道真的沒有辦法做到這一點優雅的函數沒有專精,沒有額外的膨脹?

編輯#2所以我的方式解決了這個(這是顯而易見的,但不知何故逃過了我......)是定義bool ASSortable::operator<(const ASSortable& _o) const {return this->before(_o);};該做的工作,使代碼乾淨(再一次)。

沒有更多if s或#ifdef s或我的代碼中的任何類似的混亂!

不能相信我甚至問這個問題,因爲它有這樣一個明顯的和簡單的答案:(

+1

難道你不能使用「MyClass」的特化? – 2012-07-12 12:44:35

+0

我試圖不要污染太多專業化的實施。考慮到MyClass將專門用於很多基本類型,並且代碼太雜亂(專業化將覆蓋char/short/int/long/long long和它們的未簽名對應,float/double/long double) d真的只是有一個條件編譯,而不是複製基本類型的每個專業化代碼塊 – YePhIcK 2012-07-12 12:47:34

+0

@YePhIcK您總是可以使用'enable_if'來防止爲一個專門的過載專門化一個類。我的解決方案做到了。 – pmr 2012-07-12 13:17:37

回答

1

Preproccessor在編譯的初期運行,編譯器將分析的類型和知道的std::is_fundamental<T>::value含義之前,因此它不能以這種方式工作

相反,使用專業化:

template<bool> void f(); 

template<> void f<true>() { 
    if(m_buf[j] < m_buf[idx_min]) 
     idx_min = j; 
} 

template<> void f<false>() { 
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]); 
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]); 
    // error checking removed for simplicity 
    if(curr.before(curr_min)) 
     idx_min = j; 
} 

template <typename T> 
void MyClass<T>::foo() 
{ 
// ... some code here 
    f<std::is_fundamental<T>::value>(); 
} 

編輯:你可能需要做f上午但是它不是直接可能的,因爲MyClass<T>是一個非專用模板。您可以讓f成爲全球代表,致電MyClass的正確成員。但是,還有另一種方法。

使用重載,這成爲:

void MyClass<T>::f(const true_type&) { 
    if(m_buf[j] < m_buf[idx_min]) 
     idx_min = j; 
} 

void MyClass<T>::f(const false_type&) { 
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]); 
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]); 
    // error checking removed for simplicity 
    if(curr.before(curr_min)) 
     idx_min = j; 
} 

template <typename T> 
void MyClass<T>::foo() 
{ 
// ... some code here 
    f(std::is_fundamental<T>::type()); 
} 
+0

@DavidRodríguez-dribeas:不正確,除非你明確實例化它們,否則它們不會被實例化。 – ybungalobill 2012-07-12 15:26:35

+0

對不起,我不知道我在想什麼。 – 2012-07-12 15:50:04

0

這幾乎是它說什麼,你不能在預處理指令使用::。實際上,#if之後唯一可以使用的是在編譯時間之前定義的常量表達式。你可以找到一些信息here

0

std::is_fundamental<T>::value == true不能在預處理時使用。我想你將不得不使用一些SFINAE技巧與std :: enable_if:

template <typename T> 
typename std::enable_if<std::is_fundamental<T>::value, void>::type 
MyClass<T>::foo() 
{ 
    // ... some code here 

    if(m_buf[j] < m_buf[idx_min]) 
     idx_min = j; 
} 


template <typename T> 
typename std::enable_if<!std::is_fundamental<T>::value, void>::type 
MyClass<T>::foo() 
{ 
    // ... some code here 

    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]); 
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]); 
    // error checking removed for simplicity 
    if(curr.before(curr_min)) 
     idx_min = j; 
} 
+3

Afaik這不會工作,除非你讓'foo'依賴於模板參數。 – pmr 2012-07-12 13:04:51

1

你正在混合的編譯狀態。預處理器在實際編譯器之前運行,並且不知道類型或模板。它只是執行(非常)複雜的文本替換。

在當前的C++中,沒有什麼比如static if ,因此您必須使用不同的方法來啓用條件編譯。對於功能我寧願enable_if

#include <type_traits> 
#include <iostream> 

template <typename T> 
struct Foo 
{ 
    template <typename U = T, 
      typename std::enable_if< 
       std::is_fundamental<U>::value, int >::type = 0 
      > 
    void foo() { 
    std::cout << "is fundamental" << std::endl; 
    } 

    template <typename U = T, 
      typename std::enable_if< 
       !(std::is_fundamental<U>::value), int >::type = 0 
      > 
    void foo() { 
    std::cout << "is not fundamental" << std::endl; 
    } 
}; 


struct x {}; 

int main() 
{ 
    Foo<int> f; f.foo(); 
    Foo<x> f2; f2.foo(); 
    return 0; 
} 

參考文獻:

Video:靜若在融入本土呈現由Alexandrescu的。

n3322:沃爾特·E.布朗對static if建議

n3329:薩特,明亮Alexandrescu的提案爲static if

+0

太糟糕'靜態如果'是不會很快可用:( – YePhIcK 2012-07-12 14:58:36

+0

@YePhIcK:'靜態如果'有它自己的問題。它會允許某些構造,可以混​​淆的用戶,很難跟蹤編譯器,我不記得這些細節,但是直到實例化模板(查找的第二階段)之前,類型的實際內容是未知的(某些字段或屬性可能會被「static if」語句改變) – 2012-07-12 15:17:37

2

常見的模式來解決這一問題正朝着功能的基類是專業化,濫用繼承將其帶入您的範圍:

template <typename T, bool is_fundamental> 
struct Foo_impl { 
    void foo() { 
    } 
}; 
template <typename T> 
struct Foo_impl<T,true> 
{ 
    void foo() {    // is fundamental version 
    } 
}; 
template <typename T> 
class Foo : public Foo_impl<T, std::is_fundamental_type<T>::value> { 
    // ... 
}; 

另一種方法是將這些私有函數實現爲您的c根據這個特點,從foo內部向他們發送內容。這非常簡單,並且是一個更清潔的解決方案,但如果foo_impl的兩個版本中的一個版本不能編譯,則會失敗。在這種情況下,您可以使用,因爲其他人已經建議使用模板成員函數來解析,但是我仍然會提供非模板foo作爲公共接口,轉發到私有模板foo_impl。原因在於template中有一個實現細節hack條件編譯,而不是接口的一部分。您不希望用戶代碼使用不同於您自己的類的類型的模板參數來調用該成員函數。從PMR的回答借鑑:

template <typename T> 
struct Foo 
{ 
    template <typename U = T, 
      typename std::enable_if< 
       std::is_fundamental<U>::value, int >::type* _ = 0 
      > 
    void foo() { 
    std::cout << "is fundamental" << std::endl; 
    } 
//... 

該解決方案允許用戶代碼,如:

Foo<int> f; 
f.foo<std::string>(); 

這將實例化你不需要也不想要的功能,並且將執行,你不邏輯想。即使用戶不想欺騙你的類,接口中的模板事實可能會令人困惑,並使用戶認爲可以將其稱爲不同類型。

+0

是的,這對我的解決方案來說是一個真正的問題。您的API更智能,更安全,更清潔。 – pmr 2012-07-12 14:58:03