I have a function named 'equal' accepting 2 parameters, each of them should be either std::set or std::multiset, and the element type of container should be arithmetic type (int, float, double ... ). I want the compiler report an error if above 2 conditions not satisfied.
I hope my code could run like this:
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;
}
And now it can check if element is arithmetic type, like below:
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;
}
But how to make sure the container only be set or multiset ?
the compiler can support C++11, such as vc2015 or gcc4.8
Create a is_set_or_multiset
type trait that will use template specialization to match std::set<...>
and std::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 {};
Then use it as an additional condition in your enable_if
clause:
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
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;
}
The static_assert
s are just to verify the SFINAE above it. The SFINAE can be skipped if you don't need to have SFINAE -- if you are ok with hard build breaks instead of failure to match this overload.
Note that the enable_if_t<>* =nullptr
technique doesn't work perfectly on some compilers, like MSVC2015, in my experience. On those compilers, use class=enable_if_t<>
. I use enable_if_t<>* =nullptr
because it gets rid of the "identical template signature" problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With