2017-08-26 102 views
12

我有一個其中包含std::variant的類。這個std::variant類型只允許保存特定的類型列表。如何檢查std :: variant是否可以保存某種類型

我有模板功能,允許類的用戶插入各種值到std::unordered_map,地圖保存這種變體類型的值。即,如果用戶的類型在特定的類型列表中,則只允許插入值。但是,我不希望用戶能夠自己定義這個類型的列表。

class GLCapabilities 
{ 
public: 
    using VariantType = std::variant<GLint64>; // in future this would have other types 

    template <typename T> 
    std::enable_if_t<???> AddCapability(const GLenum parameterName) 
    { 
     if(m_capabilities.count(parameterName) == 0) 
     { 
      /*... get correct value of type T ... */ 
      m_capabilities.insert(parameterName,value); 
     } 
    } 

    template<typename T> 
    std::enable_if_t<???,T> GetCapability(const GLenum parameterName) const 
    { 
     auto itr = m_capabilities.find(parameterName); 
     if(std::holds_alternative<T>(*itr)) 
      return std::get<T>(*itr); 

     return T; 
    } 

private: 
    std::unordered_map<GLenum,VariantType> m_capabilities; 
}; 

你會在上面看到有???,我該怎麼檢查? std::disjunctionstd::is_same的組合?

std::enable_if<std::disjunction<std::is_same<T,/*Variant Types???*/>...>> 

要清楚,我寧願不用手動檢查對每個允許的類型。

+0

不會'm_capabilities.insert('簡單地編譯失敗​​是一個無效的值被傳遞了嗎?在我看來,簡單的鴨子打字就是這裏的簡單答案,如果你使用'emplace )',顯然,還是你想防止任何隱式轉換? – Frank

+0

因爲我會使用各種OpenGL類型,比如'GLuint'或者'GLint'等,隱式轉換可能是一個問題;所以是的,我會就像避免它一樣 – NeomerArcana

+0

在Boost.Variant中我們有'VariantType :: types'來獲得一個'mpl :: list'類型,然後你可以使用'mpl :: contains'。這似乎在'std: :variant',必須通過推導模板參數並使用可變參數模板來實現它。 – alfC

回答

8

編輯:我實際上挖掘你的想法std::disjunction,它絕對有效。您只需使用模板專業化提取類型列表。

我的整個老派的遞歸混亂變得簡單:

template<typename T, typename VARIANT_T> 
struct isVariantMember; 

template<typename T, typename... ALL_T> 
struct isVariantMember<T, std::variant<ALL_T...>> 
    : public std::disjunction<std::is_same<T, ALL_T>...> {}; 

原來的答覆:下面是一個簡單的模板,完成此。它通過返回false爲空類型列表工作。對於非空列表,如果第一個類型通過std::is_same<>,則返回true,否則遞歸調用除第一個類型以外的所有其他類型的列表。

#include <vector> 
#include <tuple> 
#include <variant> 

// Main lookup logic of looking up a type in a list. 
template<typename T, typename... ALL_T> 
struct isOneOf : public std::false_type {}; 

template<typename T, typename FRONT_T, typename... REST_T> 
struct isOneOf<T, FRONT_T, REST_T...> : public 
    std::conditional< 
    std::is_same<T, FRONT_T>::value, 
    std::true_type, 
    isOneOf<T, REST_T...> 
    >::type {}; 

// Convenience wrapper for std::variant<>. 
template<typename T, typename VARIANT_T> 
struct isVariantMember; 

template<typename T, typename... ALL_T> 
struct isVariantMember<T, std::variant<ALL_T...>> : public isOneOf<T, ALL_T...> {}; 

// Example: 
int main() { 
    using var_t = std::variant<int, float>; 

    bool x = isVariantMember<int, var_t>::value; // x == true 
    bool y = isVariantMember<double, var_t>::value; // y == false 

    return 0; 
} 

N.B.確保在調用此方法之前從T中剝離cv和引用限定符(或者將剝離添加到模板本身)。這取決於你的需求,真的。

+0

您甚至不需要定義類模板在最後的例子中樂。只需聲明它。你可以去除那個'{}',這樣就沒有人會用'std :: variant'的類型來使用它。 – skypjack

+0

@skypjack好的。固定 – Frank

+0

@真棒,真棒。你應該編輯你的答案,把你的編輯放在最上面。 – NeomerArcana

0

您可以嘗試使用SFINAE,通過構建T類型的VariantType

template <typename T, typename = VariantType(std::declval<T>())> 
void AddCapability(T const& t); // not sure how you want to use it. 

或使用std::is_constructible<VariantType, T>。畢竟你可能想知道你是否可以從類型中分配/初始化,而不是如果type實際上是變體類型之一(這是更具限制性的)。

+0

隱式轉換將啓動,OP明確希望避免。 – Frank

+0

@Frank。好的,是的,這個問題並不清楚。我想這是一個不必要的限制。 OP應該重新考慮這種任意限制。 – alfC

+0

您應該閱讀OP中的評論鏈,瞭解他爲什麼需要它。 – Frank

2

你能避免使用std::enable_if_t,並改用了經典的基於decltype表達SFINAE像在下面的例子:

#include<variant> 
#include<utility> 

struct S { 
    using type = std::variant<int, double>; 

    template<typename U> 
    auto f() 
    -> decltype(std::declval<type>().emplace<U>(), void()) { 
     // that's ok 
    } 
}; 

int main() { 
    S s; 
    s.f<int>(); 
    //s.f<char>(); 
} 

如果切換到最後一行的評論,你會得到一個編譯時時間錯誤char不是您的變體所接受的類型。

此解決方案的優點是它很簡單,您不必包括type_traits(已授予,您必須包括utility),也不需要使用支持類來獲取bool值來測試它。
當然,您可以根據您對每個功能的要求來相應地調整返回類型。

查看並運行wandbox


否則,如果你可以的std::holds_alternative限制棒(是形成不良的通話,如果該類型比較比變異的參數列表一次),注意,這是一個constexpr功能,它只是做你想要的:

#include<type_traits> 
#include<variant> 
#include<utility> 

struct S { 
    using type = std::variant<int, double>; 

    template<typename U> 
    std::enable_if_t<std::holds_alternative<U>(type{})> 
    f() { 
     // that's ok 
    } 
}; 

int main() { 
    S s; 
    s.f<int>(); 
    //s.f<char>(); 
} 

如上所述,切換註釋,你會得到一個編譯時錯誤,如預期。

查看並運行於wandbox

1

既然你已經使用C++ 17倍表達式使它更容易些:

template <class T, class U> struct is_one_of; 

template <class T, class... Ts> 
struct is_one_of<T, std::variant<Ts...>> 
: std::bool_constant<(std::is_same_v<T, Ts> || ...)> 
{ }; 

爲了增加可讀性,你可以在你的類添加一個別名:

class GLCapabilities 
{ 
public: 
    using VariantType = std::variant<GLint64>; // in future this would have other types 
    template <class T> using allowed = is_one_of<T, VariantType>; 

    template <typename T> 
    std::enable_if_t<allowed<T>{}> AddCapability(const GLenum parameterName) 
    { ... } 
}; 
3
template <class T> struct type {}; 
template <class T> constexpr type<T> type_v{}; 

template <class T, class...Ts, template<class...> class Tp> 
constexpr bool is_one_of(type<Tp<Ts...>>, type<T>) { 
    return (std::is_same_v<Ts, T> || ...); 
} 

然後在enable_if中使用is_one_of(type_v<VariantType>, type_v<T>)

相關問題