Search code examples
c++templatesc++11enable-if

enable_if to conditionally include member functions


I have a templated class, whose types are iterators. I want to enable/disable particular member functions depending on the iterator_category of my template parameter. In particular, I want to enable operator-- if the template parameter is a bidirectional iterator. My attempt was this:

    typename std::enable_if<
       std::is_base_of<std::bidirectional_iterator_tag,
                    MyTemplateParameter>::value,
    MyType&>::type
    operator --() {
    //do work
    return *this;
  }

Clang tells me (roughly): error: no type named 'type' in 'std::__1::enable_if<false, MyTemplateParameter>'; 'enable_if' cannot be used to disable this declaration

Is there a way to accomplish what I'm trying?

Here's the example in some context:

    #include <iterator>
    #include <type_traits>

    template <typename TagType> 
    class test {
      public:
      typename std::enable_if<
         std::is_base_of<std::bidirectional_iterator_tag,
                        TagType>::value,
        test>::type
      operator --() {
         return *this;
      }

    };

    int main(){

      test<std::random_access_iterator_tag> t1;
      test<std::forward_iterator_tag> t2;

    /*
    breakTemps.cpp:13:2: error: no type named 'type' in 'std::__1::enable_if<false,     test<std::__1::forward_iterator_tag> >'; 'enable_if' cannot be used to disable this declaration
            std::is_base_of<std::bidirectional_iterator_tag,
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    breakTemps.cpp:25:35: note: in instantiation of template class 'test<std::__1::forward_iterator_tag>' requested here
       test<std::forward_iterator_tag> t2;
                                       ^
    */

}

Solution

  • std::enable_if needs to depend on a parameter of the member template itself.

    template <typename TagType>
    class foo
    {
    public:
        template <typename U = TagType>
          typename std::enable_if<
             std::is_base_of<std::bidirectional_iterator_tag,
                            U>::value,
            foo>::type
          operator --() {
             return *this;
          }
    };
    

    SFINAE will work as expected.

    int main() {
      foo<std::random_access_iterator_tag> f;
      foo<std::forward_iterator_tag> f2;
      --f; // fine
      --f2;
    }
    
    main.cpp:24:3: error: no match for 'operator--' (operand type is 'foo<std::forward_iterator_tag>')
    
    --f2;