I have a template function that I want to enable only for standard containers (or containers compatible with standard containers, which at least provide a begin()
member function). I'm SFINAE-ing out non-containers in the following way:
template<typename Container>
typename Container::value_type
f(const Container& c,
typename std::enable_if<
std::is_same<
decltype(*c.begin()),
typename Container::value_type
>::value
>::type* = nullptr)
{
// implementation here
}
The std::is_same
and decltype
don't look too elegant. Is there any better way of doing this?
PS: I need the SFINAE here because I have a different overload
template<typename Derived>
f(const Eigen::MatrixBase<Derived>& A)
and whenever I try f(some_Eigen_matrix)
, the Container
overload ends up being picked up, then the compiler spits out an error because the type is lacking begin()
.
Using void_t
, we can just make a type trait for having begin()
and end()
(and anything else you might want to check for, like typename T::iterator
, you can just keep piling expressions on):
template <typename T, typename = void>
struct is_std_container : std::false_type { };
template <typename T>
struct is_std_container<T,
void_t<decltype(std::declval<T&>().begin()),
decltype(std::declval<T&>().end()),
typename T::value_type
>>
: std::true_type { };
And then just SFINAE on that:
template <typename Container>
typename std::enable_if<
is_std_container<Container>::value,
typename Container::value_type
>::type
f(const Container& c) { .. }
Also, if you really wanted to verify that begin()
gives you back a T::iterator
(or at least that they're equality comparable), you can do that too:
void_t<
decltype(begin(std::declval<T&>()) == std::declval<typename T::iterator>())
>
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