Joe Silver
Joe Silver

Reputation: 50

How to enable a template class specialization when the template parameter is a bidirectional_iterator?

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

Answers (1)

Barry
Barry

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

Related Questions