Steve
Steve

Reputation: 12004

bind with enable_if'd member functions

I'm getting tripped up on calling bind on an enable_if'd member function. I've worked around it using if constexpr, but I'm curious what the solution would be for this issue. The code is the simplest reproducer and not representative of the overarching problem I'm trying to solve. How can I make this work?

#include <iostream>
#include <type_traits>                                                                                                                                                                                                                                            
#include <functional>                                                                                                                                                                                                                                       

template <typename T>                                                                                                                                                                                                                                           
class test {                                                                                                                                                                                                                                                    
public:                                                                                                                                                                                                                                                         

  template <typename U = T>                                                                                                                                                                                                                                     
  typename std::enable_if<std::is_copy_constructible<U>::value, void>::type                                                                                                                                                                                     
  foo(const T& v){
      std::cout << "copy constructible";
  }                                                                                                                                                                                                                                              

  template <typename U = T>                                                                                                                                                                                                                                     
  typename std::enable_if<!std::is_copy_constructible<U>::value, void>::type                                                                                                                                                                                     
  foo(const T& v){
      std::cout << "not copy constructible";
  }                                                                                                                                                           

  void foo_bar(const T& v){
      std::cout << "test";
  }
};                                                                                                                                                                                                                                                              


int main(int argn, char** argc){                                                                                                                                                                                                                                
                                                                                                                                                                                                                                             
  test<int> myfoo;           
  myfoo.foo(3); //Works           
  auto func = std::bind(&test<int>::foo_bar, &myfoo, 3);  //Works                                                                                                                                                                                                                      
  auto func = std::bind(&test<int>::foo, &myfoo, 3); //Doesn't work                                                                                                                                                                                             

  func();                                                                                                                                                                                                                                                       
  return 0;                                                                                                                                                                                                                                                     

}                                                                                                                                                                                                                                                               

Upvotes: 1

Views: 59

Answers (1)

user12002570
user12002570

Reputation: 1

The problem is that U is substituted only when the member function template foo is actually called, and not when it is used inside of std::bind(). This means that when specifying &test<int>::foo inside bind(), it isn't known which of the two member function templates is going to be actually instantiated since the instantiation of definition will happen when we actually call foo(), and so the compiler can't decide which of them you're referring to. This is exactly what <unresolved overloaded function type> means in the error that you're getting:

no matching function for call to 'bind(<unresolved overloaded function type>, test<int>*, int)'
   32 |   auto func2 = std::bind(&test<int>::foo, &myfoo, 3); //Doesn't work
      |                ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can solve this by either specifying U explicitly, like:

&test<int>::foo<int>

or by using a lambda, like:

auto func = [&myfoo]() { myfoo.foo(3); };


There is a third way to solve this in C++20 using requires-clause as shown below. Note that the below program is well-formed but gcc and msvc incorrectly rejects it. Only clang accepts it which is the correct behavior. Demo

#include <iostream>
#include <type_traits>                                                                                                                                                                                                                                            
#include <functional>                                                                                                                                                                                                                                       

template <typename T>                                                                                                                                                                                                                                           
class test {                                                                                                                                                                                                                                                    
public:                                                                                                                                                                                                                                                         

                                                                                                                                                                                                                                                                                                                                                                                                                         
  void foo(const T& v) requires std::is_copy_constructible_v<T>{
      std::cout << "copy constructible";
  }                                                                                                                                                                                                                                              
  void foo(const T& v) requires (!std::is_copy_constructible_v<T>) {
      std::cout << "non copy constructible";
  } 
                                                                                                                                                           

  void foo_bar(const T& v){
      std::cout << "test";
  }
};                                                                                                                                                                                                                                                              

int main(int argn, char** argc){                                                                                                                                                                                                                                
                                                                                                                                                                                                                                             
  test<int> myfoo;           
  myfoo.foo(3); //Works           
  auto func = std::bind(&test<int>::foo_bar, &myfoo, 3);  //Works                                                                                                                                                                                                                      
  auto func2 = std::bind(&test<int>::foo, &myfoo, 3); //works                                                                                                                                                                                           

  func(); 
  func2();                                                                                                                                                                                                                                                      

}  

Upvotes: 2

Related Questions