Search code examples
c++c++17sfinae

How can I use enable_if to enable a template function based on what type an iterator iterates over?


I currently have a function that looks like this:

    template<typename input_iterator>
    std::string doit(input_iterator beg, input_iterator end)
    {
        static_assert(sizeof(*beg) == 1, "Size one only, please");
        return do_something(beg, end);
    }

This works fine in that

std::vector<uint8_t> v1 {1, 2, 3};
doit(v1.begin(), v1.end());

succeeds while

std::vector<int> v2 {1, 2, 3};
doit(v2.begin(), v2.end());

fails at the static_assert.

What I would prefer is to have the code fail at the call site rather than within the called function, and get rid of the static_assert.

I have tried a number of things including variations on enable_if but I haven't hit on the right combination. My latest attempt was

    template<typename input_iterator, 
       std::enable_if<sizeof(std::iterator_traits<input_iterator>::value_type) == 1>>
    std::string doit(input_iterator beg, input_iterator end)
    {
        return do_something(beg, end);
    }

but this resulted in no-matching-function failures no matter what type my iterators are.

Can someone please point me in the right direction? I'm not looking for concepts, I need code that will compile using C++17.

Thanks.


Solution

  • This seems to do the trick:

    https://godbolt.org/z/6GjcoYrzG

    #include <string>
    #include <vector>
    #include <stdexcept>
    #include <type_traits>
    
    template<typename input_iterator>
    auto doit(input_iterator beg, input_iterator end)
     -> std::enable_if_t<sizeof(*beg) == 1, std::string>
    {
        throw std::runtime_error("");
        // return do_something(beg, end);
    }
    
    int main() {
        std::vector<uint8_t> v1 {1, 2, 3};
        doit(v1.begin(), v1.end());
    
        std::vector<int> v2 {1, 2, 3};
        doit(v2.begin(), v2.end());  // error: no matching function for call to 'doit'
    
        return 0;
    }