2016-04-12 32 views
10

下面可變參數模板SFINAE代碼編譯很好用鏗鏘3.7.1,C++ 14:SFINAE不是性病發生:: underlying_type

#include <array> 
#include <iostream> 
#include <vector> 
#include <cstdint> 

enum class Bar : uint8_t { 
    ay, bee, see 
}; 

struct S { 

static void foo() {} 

// std::begin(h) is defined for h of type H 
template<typename H, typename... T> 
static typename std::enable_if<std::is_pointer<decltype(std::begin(std::declval<H>()))*>::value>::type 
foo(const H&, T&&... t) 
{ std::cout << "container\n"; foo(std::forward<T>(t)...); } 

// H is integral 
template<typename H, typename... T> 
static typename std::enable_if<std::is_integral<typename std::remove_reference<H>::type>::value>::type 
foo(const H&, T&&... t) 
{ std::cout << "integer\n"; foo(std::forward<T>(t)...); } 

// H is an enum with underlying type = uint8_t 
/* 
template<typename H, typename... T> 
static typename std::enable_if<std::is_same<typename std::underlying_type<H>::type,uint8_t>::value>::type 
foo(const H&, T&&... t) 
{ std::cout << "enum\n"; foo(std::forward<T>(t)...); } 
*/ 
}; 


int main() 
{ 
    S::foo(std::array<int,8>(), 5, 5L, std::vector<int>{}, 5L); 
} 

我想要的foo正確超載遞歸調用,根據在類型H

  1. 如果std::begin(h)H類型的h被定義,我想選擇
  2. 的 過載數
  3. 如果H是一個「整體式」,我想超載數2

這工作,因爲它是。但是,如果我添加其他重載枚舉類型(你可以嘗試取消註釋第三過載),然後我得到:

​​

我同意,只有枚舉有一個基本的類型,因此爲什麼是不是第三次超載(使用std::underlying_type)將SFINAE-d帶走?

+0

你爲什麼不乾脆用'的std :: enable_if <性病:: is_enum ::值> :: type'?它[編譯好](http://ideone.com/AeUzi9)。你是否僅僅具有'uint8_t'的基礎類型的'enum'?在這種情況下,你可以再添加1個'sizeof(H)== sizeof(uint8_t)'的條件。即:'std :: is_enum :: value &&(sizeof(H)== sizeof(uint8_t))''。這在上面的ideone例子中已經介紹過了。 – iammilind

+1

@iammilind:很好的建議,謝謝 –

回答

13

std::underlying_type不是SFINAE友好的。嘗試訪問std::underlying_type<T>::type以獲取非枚舉類型會導致未定義的行爲(通常是硬錯誤),而不是替代失敗。

在嘗試訪問其基礎類型之前,您需要確定所討論的類型是否爲枚舉類型。按照這種方式編寫這個文件將會沿着typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type。用這個可怕的混亂替換typename std::underlying_type<H>::type在你的回報類型,你會得到一個更可怕的混亂,工程:)

如果你發現自己需要經常這樣做 - 或只是不想寫typename std::enable_if<std::is_same<typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type, uint8_t>::value>::type - 你可以寫一個SFINAE友好underlying_type

template<class T, bool = std::is_enum<T>::value> 
struct safe_underlying_type : std::underlying_type<T> {}; 
template<class T> 
struct safe_underlying_type<T, false /* is_enum */> {};