enable_if iterator as a default template parameter?

前端 未结 1 1208
有刺的猬
有刺的猬 2020-12-16 08:43

I have a constructor like that :

class MyClass
{
    template MyClass(TI first, TI last);
};

template MyClass::MyClass(TI fi         


        
相关标签:
1条回答
  • 2020-12-16 08:50

    It depends on what you want. If there are no other overloads, it can be ok with just nothing at all. The compiler will produce an error if a type is passed that doesn't provide the necessary operation.

    If you really want to limit it to iterators, it's preferable to do so with a static_assert, since it produces an error with a nice custom error message, instead of "ambiguous function call, here are all the gazillion overloads I could find: follows endless list of overloads" or "could not find function, find it yourself".

    If there is another templated overload that conflicts, then you do indeed need some enable_if thing. I wrote a blog post about using enable_if with C++11 features, and why default template parameters are not very good for that. I settled with something like this instead:

    enum class enabler {};
    
    template <typename Condition>
    using EnableIf = typename std::enable_if<Condition::value, enabler>::type;
    
    
    class MyClass
    {
        template<class TI, EnableIf<is_iterator<TI>>...> MyClass(TI first, TI last);
    };
    
    template<class TI, EnableIf<is_iterator<TI>>...> MyClass::MyClass(TI first, TI last)
    { /* blah */ }
    

    All that you need now is a trait for the test. I think testing for the existence of iterator_category is enough, but it should be done with std::iterator_traits, because pointers are iterators and don't have nested typedefs.

    That can be done with the usual techniques that use SFINAE. With C++11, I do the following:

    template <typename T>
    struct sfinae_true : std::true_type {};
    
    struct is_iterator_tester {
        template <typename T>
        static sfinae_true<typename std::iterator_traits<T>::iterator_category> test(int);
    
        template <typename>
        static std::false_type test(...);
    };
    
    template <typename T>
    struct is_iterator : decltype(is_iterator_tester::test<T>(0)) {};
    

    All that said, this could have been done with the traditional technique of using a defaulted function parameter:

    class MyClass
    {
        template<class TI>
        MyClass(TI first, TI last,
                typename std::iterator_traits<T>::iterator_category* = nullptr)
    };
    
    template<class TI>
    MyClass::MyClass(TI first, TI last,
                     typename std::iterator_traits<T>::iterator_category*)
    { /* blah */ }
    
    0 讨论(0)
提交回复
热议问题