3
votes

Please take a look at the following code below. In the case of a capture lambda expression it will work and compile just fine. Although when using a bind callable expression I will get an overload expression expression error

main.cpp:13:60: error: decltype cannot resolve address of overloaded function 
 using FuncType = decltype(&std::decay<T>::type::operator()); 

How can I code this such that I am getting the type of the operator() method for the bind function? The bind type is std::_Bind<void (*(std::_Placeholder<2>, std::_Placeholder<1>))(int, int)> which I also don't fully understand; I know void (*) (int, int) as a type as a pointer to function taking in int, int and returning void. I guess I don't understand the syntax void *(x,y) (int,int); basically the x,y part. I was assuming that after all the template stuff there is a method void operator()(int, int) that will get resolved that bind_f(x,y) will call and I was trying to capture that type.

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

void tester(int x, int y) {
   std::cout << " x = " << x << " y = " << y << std::endl;
}

template <typename T>
class TypeChecker;

template <typename T>
using FuncType = decltype(&std::decay<T>::type::operator());

int main()  {
   using namespace std::placeholders;
   auto bind_f = std::bind(tester, _2, _1);
   bind_f(1,2);                            

   int y = 5;                              

   auto lambda = [y]() {                   
      std::cout << " y = " << y << std::endl;
   };                                        

   typedef FuncType<decltype(lambda)> x1;            
   typedef FuncType<decltype(bind_f)> x2;            

   //TypeChecker<decltype(bind_f)> t2;       

}                                            
1
A bind expression has a templated call operator that accepts any number and kind of arguments. A non-polymorphic lambda has a single, non-template call operator.Kerrek SB
May I ask, why you want to use bind in the first place? Or is this just for edification? I'm not saying the question is bad, don't misunderstand, just pointing out that the very practical answer is "don't use bind", as its use is recommended against in favor of lambdas (esp in 14 which has generic lambdas).Nir Friedman
That makes sense, yeah I was creating something that covered all cases but yeah in general I would like to make it not useable with bind I just thought someone might ask for it to work with bind. I am wondering if there is a way to make the FuncType such that it will allow both types like template <typename T, typename... Ts> using FuncType = decltype(T::operator()(std::declval<Ts>()...))(Ts...); bjackfly

1 Answers

0
votes

Like the commenters said, the operator() on your bind object is a templated (i.e. overloaded) function that you cannot simply bind to without choosing the version you want.

We can fix this by adding another template argument to FuncType, static_cast'ing to select the appropriate function, and then being explicit when we make our typedefs:

template <typename T, typename... U>
using FuncType = decltype(static_cast<void(T::*)(U...) const>(&std::decay<T>::type::operator()));

and then

auto bind_f = std::bind(tester, _2, _1);                         

int y = 5;                              

auto lambda = [y]() {                   
   std::cout << " y = " << y << std::endl;
};                                        
   
using x1 = FuncType<decltype(lambda)>;
using x2 =  FuncType<decltype(bind_f), int&&, int&&>;
   
x1 p = &decltype(lambda)::operator();
(lambda.*p)();
   
x2 q = &decltype(bind_f)::operator();
(bind_f.*q)(1,2);

Live Demo

Output:

y = 5

x = 2 y = 1

I agree with Nir Friedman when he said to just avoid bind altogether; lambdas make things way easier.