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

Overloading function where input has certain member function


I'm attempting to overload a function depending on whether the passed in sequence container has push_back as a member function.

#include <vector>
#include <forward_list>
#include <list>
#include <array>
#include <iostream>

template<class T, typename std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::push_back)>, int> = 0>
void has_push_back(T p)
{
  std::cout << "Has push_back" << std::endl;
}

template<class T, typename std::enable_if_t<!std::is_member_function_pointer_v<decltype(&T::push_back)>, int> = 0>
void has_push_back(T p)
{
  std::cout << "No push_back" << std::endl;
}

int main() {
  std::vector<int> vec = { 0 };
  std::array<int, 1> arr = { 0 };
  std::forward_list<int> f_list = { 0 };

  has_push_back(vec);
  has_push_back(arr);
  has_push_back(f_list);
  
  return 0;
}

This results in the following compiler error for each of the calls to has_push_back():

error C2672: 'has_push_back': no matching overloaded function found
error C2783: 'void has_push_back(T)': could not deduce template argument for '__formal'

Expected result:

Has push_back
No push_back
No push_back

Solution

  • You could use expression SFINAE for this:

    #include <iostream>
    #include <vector>
    #include <array>
    #include <forward_list>
    
    void has_push_back(...)
    {
      std::cout << "No push_back" << std::endl;
    }
    
    template<class T>
    auto has_push_back(T&& t) 
      -> decltype(t.push_back(t.front()), void()) 
      // ^^ expression SFINAE, only considered 
      // if the whole expression within decltype is valid
    {
      std::cout << "Has push_back" << std::endl;
    }
    
    int main() {
      std::vector<int> vec = { 0 };
      std::array<int, 1> arr = { 0 };
      std::forward_list<int> f_list = { 0 };
    
      has_push_back(vec);
      has_push_back(arr);
      has_push_back(f_list);
      
      return 0;
    }
    

    Result:

    Has push_back
    No push_back
    No push_back
    

    https://godbolt.org/z/dbzafs1nY

    &T::push_back may not be well formed when push_back is a template or overloaded. Instead, I check if a call with t.front() to push_back is valid or not. Of course this also requires the type to have a suitable front member.