Reputation: 12004
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
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