2017-03-09 10 views
4

我有一個名爲'equal'的函數接受2個參數,它們每個都應該是std :: set或std: :multiset,並且容器的元素類型應該是算術類型(int,float,double ...)。如果上述2個條件不滿足,我希望編譯器報告錯誤。在編譯時檢查是否設置了模板參數類型或multiset,並且容器的元素類型是數學運算

我希望我的代碼可以運行這樣的:

int main(void) 
{ 
    std::set<int> s1; 
    std::set<int> s2; 
    equal(s1, s2); // OK 

    std::multiset<float> s3; 
    std::multiset<float> s4; 
    equal(s3, s4); // OK 

    std::set<int> s5; 
    std::multiset<int> s6; 
    equal(s5, s6); // compile error 

    std::set<int*> s7; 
    std::set<int*> s8; 
    equal(s7, s8); // compile error 

    std::vector<int> s9; 
    std::vector<int> s10; 
    equal(s9, s10); // compile error 

    return 0; 
} 

現在它可以檢查是否元素是算術類型,象下面這樣:

template <class Container, class = typename std::enable_if<std::is_arithmetic<typename Container::value_type>::value>::type> 
bool equal(const Container &container1, const Container &container2) 
{ 
    return true; 
} 

但如何確保容器只能是設置還是multiset?

編譯器可以支持C++ 11,如vc2015或gcc4.8

+0

首選'typename std :: enable_if :: type * = nullptr' over'typename = typename std :: enable_if :: type'。用你的方式'等於(42,51);'是有效的,第二點,如果你想禁用部分,你會感到尷尬,因爲默認類型不是簽名的一部分。 – Jarod42

回答

2
template<template<class...>class Z, class T> 
struct is_instance_of_template : std::false_type {}; 

template<template<class...>class Z, class...Ts> 
struct is_instance_of_template<Z,Z<Ts...>> : std::true_type {}; 

template<class Container> 
using value_type_t = typename Container::value_type; 

template <class Container, 
    std::enable_if_t< 
     std::is_arithmetic<value_type_t<Container>>{} 
     && (
      is_instance_of_template<std::set, Container>{} 
      || is_instance_of_template<std::multiset, Container>{} 
     ) 
    >* =nullptr 
> 
bool equal(const Container &container1, const Container &container2) 
{ 
    static_assert(std::is_arithmetic<value_type_t<Container>>{}, 
    "Container must contain arithmetic values" 
); 
    static_assert(
    is_instance_of_template< std::set, Container >{} 
    || is_instance_of_template< std::multiset, Container >{}, 
    "Container must be a set or multiset" 
); 
    return true; 
} 

static_assert s爲只是爲了驗證它上面的SFINAE。如果您不需要SFINAE,可以跳過SFINAE - 如果您確定硬構建斷點而不是無法匹配此超載,那麼可以跳過該步驟。

請注意,根據我的經驗,enable_if_t<>* =nullptr技術在某些編譯器(如MSVC2015)上無法正常工作。在這些編譯器上,使用class=enable_if_t<>。我使用enable_if_t<>* =nullptr,因爲它擺脫了「相同的模板簽名」問題。

+0

真的很棒的答案。正如你所說在VC2015中「enable_if_t <> * = nullptr」不起作用。 – Royt

12

創建將使用模板特匹配std::set<...>std::multiset<...>一個is_set_or_multiset型性狀:

template <typename> 
struct is_set_or_multiset : std::false_type {}; 

template <typename... Ts> 
struct is_set_or_multiset<std::set<Ts...>> : std::true_type {}; 

template <typename... Ts> 
struct is_set_or_multiset<std::multiset<Ts...>> : std::true_type {}; 

然後將其用作enable_if條款中的附加條件:

template <class Container, 
      class = typename std::enable_if< 
           std::is_arithmetic<typename Container::value_type>::value 
          && is_set_or_multiset<Container>{} 
           >::type> 
bool equal(const Container &container1, const Container &container2) 
{ 
    return true; 
} 

live example on wandbox

+0

出於好奇,你將如何在C++ 17中使用is_same_v來實現,如果是constexpr? –

+1

@Benoît:我不認爲這是'如果constexpr'的恰當用例 - 如果你想使用它,你將不得不將它放在'equal'的內部。這會使'重複''平等'的方式更加棘手,因爲如果constexpr不能從超載集中移除特定的超載,那麼不滿足''內部的條件'。 –

+0

它真的很好,只有一個類型錯誤「is_set_or_multiset {}」更改爲「is_set_or_multiset ::值」,並在VC2015上編譯確定 – Royt

相關問題