Reputation: 50
I want to create a template class which accepts only bidirectional iterators as arguments in its constructor (which are used to initialize its data members).
I'm trying to use enable_if and iterator_category for this, but I can't understand what's going wrong. I'm using both gcc 8.3.1 and clang 7 on Linux with -std=c++17. I've also tried other compilers on Compiler Explorer.
(Note: I've also tried using is_same_v instead of is_base_of_v, but with the same result, or lack of...)
#include <iterator>
#include <type_traits>
#include <vector>
template<typename It>
using it_cat = typename std::iterator_traits<It>::iterator_category;
template<typename BidIt,
typename std::enable_if_t<std::is_base_of_v<it_cat<BidIt>, std::bidirectional_iterator_tag>> = 0
>
class A {
BidIt start;
public:
// A() : start {} {}
A(BidIt s_) : start {s_} {}
};
// A<std::vector<int>::iterator> a1;
int main()
{
std::vector<int> v {0, 1, 2, 3};
A a2 {v.begin()};
}
The two commented lines were an attempt to manually instantiate an empty object of type A by explicitly passing the parameter (without success). The compiler output clearly shows that the type deduction fails:
error: no type named 'type' in 'struct std::enable_if<false, void>'
typename std::enable_if_t<std::is_base_of_v<it_cat<BidIt>, std::bidirectional_iterator_tag>> = 0
and as far as I understand the enable_if is evaluated to false.
Upvotes: 1
Views: 526
Reputation: 303527
First, you're using the trait backwards. std::is_base_of<Base, Derived>
checks if the first is a base of the second. So your check should be is_base_of_v<bidirectional_iterator_tag, it_cat<BidIt>>
.
Second, the C++17 idiom to do this kind of conditional enabling (assuming you want to have other specializations) is to have a defaulted 2nd template parameter:
template <typename T, typename Enable = void>
struct X; // the primary
template <typename T>
struct X<T, std::enable_if_t</* condition */>> // the conditional specialization
{ ... };
If you do not need a different specialization, we can do this in a simpler way:
template <typename T>
struct X {
static_assert(/* the condition */, "!");
};
Upvotes: 0