2017-03-03 41 views
1

我工作的一個項目中某一功能的行爲需要幾個值之間切換:如何使用traits來訪問編譯時const值?

class James{ 
public: 
    James(){ 
     if(a==0){ 
      //do this 
     }else{ 
      // do that 
     } 
    } 
}; 

目前,「A」是在運行時配置文件讀取。但是,實際上,'a'可以在編譯時確定,而不是運行時間。我想有一個特質類

struct TraitZero{ 
    constexpr int a = 0; 
}; 

struct TraitOne{ 
    constexpr int a = 1; 
}; 

然後把詹姆斯變成一個模板類

template<typename Trait> 
class James{ 
    constexpr int a = Trait::a; 
    public: 
     James(){ 
      if(a=0){ 
       //do this 
      }else{ 
       // do that 
      } 
     } 
    }; 

我不知道我在哪裏弄錯了,但這並不編譯。

我想知道這裏有沒有人曾經遇到這樣的問題。誰能分享一些見解?

+1

'A = 0'應該是'一個== 0' – Barry

+0

您是不是要找一個== 0? –

+1

在你的結構中''constexpr int a''是否需要是'static'?小心分享編譯錯誤? – qxz

回答

1

a數據成員必須被聲明爲constexprstatic使用您要使用它們的方式:

struct TraitZero{ 
    static constexpr int a = 0; 
}; 

struct TraitOne{ 
    static constexpr int a = 1; 
}; 

拋開事實,這是可以形成不良就目前而言,您不會以Traits::a的身份訪問它。
這同樣適用於類James

template<typename Trait> 
class James{ 
    static constexpr int a = Trait::a; 
    //... 
}; 

還要注意,可能下面是不是你想要的東西:

if(a=0){ 

即使你被允許修改a(你是不是因爲它是一個靜態的constexpr數據成員),在這種情況下,你應該分配0到a並且不斷得到else分支。
最有可能你正在尋找類似但略有不同的東西:

if(a == 0){ 

下面是根據你的代碼,一旦固定的例子:

#include<iostream> 

struct TraitZero{ 
    static constexpr int a = 0; 
}; 

struct TraitOne{ 
    static constexpr int a = 1; 
}; 

template<typename Trait> 
class James{ 
static constexpr int a = Trait::a; 
public: 
    James(){ 
     if(a==0){ 
      std::cout << "0" << std::endl; 
     }else{ 
      std::cout << "1" << std::endl; 
     } 
    } 
}; 

int main() { 
    James<TraitZero> j0; 
    James<TraitOne> j1; 
} 
+0

是的,我想要的是==而不是=。謝謝 –

1

由於has already been mentioned by skypjack,只有static數據成員,也爲constexpr,並且您需要在條件中使用==而不是=

也就是說,既然你想在編譯時確定a,那麼你也可以在編譯時根據a進行分支。爲此,您可以使用SFINAE或(自C++ 17起)constexpr if


假設以下三個特徵...

struct TraitZero{ 
    static constexpr int a = 0; 
}; 

struct TraitOne{ 
    static constexpr int a = 1; 
}; 

template<size_t N> 
struct TraitN { 
    static constexpr int a = N; 
}; 

我們能夠做到這一點的?

  • SFINAE:

    template<typename Trait> 
    class James { 
        // Unnecessary, we can access Trait::a directly. 
        //static constexpr int a = Trait::a; 
        public: 
        template<bool AZero = Trait::a == 0> 
        James(std::enable_if_t<AZero, unsigned> = 0) { 
         std::cout << "Trait::a is 0.\n"; 
        } 
    
        template<bool AOne = Trait::a == 1> 
        James(std::enable_if_t<AOne, int> = 0) { 
         std::cout << "Trait::a is 1.\n"; 
        } 
    
        template<bool ANeither = (Trait::a != 0) && (Trait::a != 1)> 
        James(std::enable_if_t<ANeither, long> = 0) { 
         std::cout << "Trait::a is neither 0 nor 1.\n"; 
        } 
    }; 
    

    這樣做是有條件地選擇基礎上的Traits::a價值James()的版本之一,使用虛擬參數來啓用重載;這對構造函數和析構函數以外的函數更簡單,因爲enable_if可以用於它們的返回類型。

    注意使用模板參數,而不是直接在enable_if中自己檢查Trait::a。由於SFINAE只能在函數的上下文中使用類型和表達式來執行,因此可以用它們「拖入」,可以這麼說;我喜歡在這樣做的時候執行邏輯,因爲它最大限度地減少了enable_if的侵入性。

  • constexpr如果:

    template<typename Trait> 
    class James { 
        // Unnecessary, we can access Trait::a directly. 
        //static constexpr int a = Trait::a; 
        public: 
        James() { 
         if constexpr (Trait::a == 0) { 
          std::cout << "Trait::a is 0.\n"; 
         } else if constexpr (Trait::a == 1) { 
          std::cout << "Trait::a is 1.\n"; 
         } else { 
          std::cout << "Trait::a is neither 0 nor 1.\n"; 
         } 
        } 
    }; 
    

    正如這裏可以看到,constexpr如果可以用來創造出比SFINAE更清潔,更自然的代碼,其優點是它仍然會在編譯的時候,而不是進行評估運行;不幸的是,它還沒有得到大多數編譯器的支持。 [在此特定情況下,James()每個版本也將是一個機器指令短(當與GCC 7.0編譯),由於不使用虛設參數重載之間進行區分來。]

    更具體地,constexpr如果,如果條件是true,如果它的false語句真被丟棄的語句假被丟棄;實際上,這基本上意味着編譯器將整個constexpr if語句看作將要執行的分支。例如,在這種情況下,編譯器將根據Trait::a的值生成以下三個函數之一。

    // If Trait::a == 0: 
    James() { 
        std::cout << "Trait::a is 0.\n"; 
    } 
    
    // If Trait::a == 1: 
    James() { 
        std::cout << "Trait::a is 1.\n"; 
    } 
    
    // If Trait::a == anything else: 
    James() { 
        std::cout << "Trait::a is neither 0 nor 1.\n"; 
    } 
    

在任一情況下,用下面的代碼...產生

int main() { 
    James<TraitZero> j0; 
    James<TraitOne> j1; 
    James<TraitN<2>> j2; 
} 

以下輸出:

Trait::a is 0. 
Trait::a is 1. 
Trait::a is neither 0 nor 1. 

每種類型的構造將是編碼專門輸出適當的行,三個構造函數都不會包含任何分支。

注意,我只標記構件a不必要出個人喜好的;因爲我可以直接訪問Trait::a,所以我寧願這麼做,所以如果我曾經放棄過,我不需要檢查什麼是a。隨意使用它,如果你想,或者如果它需要在別處。