1

I'm trying to bind a method of a class and to pass it as a template parameter to a different class. This type needs to be used in multiple methods, so I tried using a using clause in the class (struct base below). However, I'm not really able to get the type right. These are (most of) what I've tried so far:

#include <functional>
#include <utility>
#include <type_traits>

template <typename T>
struct N
{
    N(T t) {}
};

struct base
{
    float f() const { return 0.f;}

    // using myN = N<decltype(std::bind(&base::f, std::add_pointer_t<std::declval<base> > ) ) >;
    // Error: tries to convert:
    // '_Bind<float (base::*(base*))() const>' to
    // '_Bind<float (base::*(base*&& (*)()))() const>'


    // using myN = N<decltype(std::bind(&base::f, std::declval<std::add_pointer_t<base> > ) ) >;
    // Error: tries to convert: 
    // '_Bind<float (base::*(base*))() const>' to 
    // '_Bind<float (base::*(base**&& (*)()))() const>'

    using myN = N<decltype(std::bind(&base::f, std::remove_reference<std::declval<std::add_pointer_t<base> > > ) ) >;
    // Error: tries to convert:
    // '_Bind<float (base::*(base*))() const>' to
    // '_Bind<float (base::*(std::remove_reference<base*>&& (*)()))() const>'

    void g()
    {
        auto boundMethod = std::bind(&base::f, this);
        myN n(boundMethod); // <-- this is the line with the errors mentioned above.
    }
};

Limitations: I'm restricted to MSVC 2015, c++11. I've been using gcc on godbolt as quick MCVE, but the production project is with MSVC.

How do I get the types to match up?

Edit:

Using max66's solution(s) works for gcc, but fails with MSVC 2015. The error I get is

error C2664:  
'N<std::_Binder<std::_Unforced,float (__thiscall base::* )(void) const,_Ty>>::N(N<std::_Binder<std::_Unforced,float (__thiscall base::* )(void) const,_Ty>> &&)': 
cannot convert argument 1 from  
'std::_Binder<std::_Unforced,float (__thiscall base::* )(void) const,base *const >' to  
'std::_Binder<std::_Unforced,float (__thiscall base::* )(void) const,_Ty>' 
Community
  • 1
  • 1
Avi Ginsburg
  • 10,323
  • 3
  • 29
  • 56

2 Answers2

2

Why not simply std::declval<base *>() ?

using myN = N<decltype(std::bind(&base::f, std::declval<base *>()))>;

And remember you have to "call" std::declval<Type>, to obtain an object of type Type.

So std::declval<Type>(), not std::declval<Type>.

max66
  • 65,235
  • 10
  • 71
  • 111
  • 1
    Probably because I've had a long day? – Avi Ginsburg Nov 14 '19 at 18:14
  • I just tried that and it works with gcc, but with MSVC 2015 it doesn't. Neither does `std::declval(),`. See [godbolt](https://godbolt.org/z/HhS-AD) – Avi Ginsburg Nov 14 '19 at 18:20
  • @AviGinsburg - Intriguing... I see that with MSVC 19.14 compile, so I suspect a bug in preceding versions... but I'm not sure... I'll search for alternatives. – max66 Nov 14 '19 at 18:51
  • I agree that this looks like a compiler bug. I originally had a lambda based solution, but the need to use it in multiple places led me to want to use a typedef instead. – Avi Ginsburg Nov 14 '19 at 19:34
  • @AviGinsburg - I see... I'm trying the lambda way but I'm failing exactly with the part "define a typename using the lambda". – max66 Nov 14 '19 at 19:35
  • @AviGinsburg - a completely different way: what about defining `myN` simply as `N>`? Should be compatible with both `std::bind()` and lambda way. – max66 Nov 14 '19 at 19:41
  • Any idea if that would add additional overhead? – Avi Ginsburg Nov 15 '19 at 07:54
  • @AviGinsburg - Yes, I suppose an additional overhead: `std::function` should be heavy, from this point of view. But I suppose an additional overhead also from `std::bind()`. I don't know if is better `std::function` with a lambda or `std::bind()` without `std::function`. – max66 Nov 15 '19 at 13:13
  • I ended up doing some refactoring and sticking with the lambda. – Avi Ginsburg Nov 18 '19 at 08:05
2

MSVC seems to think that this is a const rvalue pointer, even though it really should not be const, so this would fix it:

using myN = N<decltype(std::bind(&base::f, std::declval<base* const>()))>;

But that might break it elsewhere. To force this to be a non-const rvalue, you can also do this:

using myN = N<decltype(std::bind(&base::f, std::declval<base*>()))>;

auto boundMethod = std::bind(&base::f, &*this);

Another solution is to bind to a reference instead (which is more natural overall), and you probably also want to add constness (which also solves the problem as it makes a non-const copy of the pointer)

using myN = N<decltype(std::bind(&base::f, std::declval<base&>()))>;
auto boundMethod = std::bind(&base::f, *this);


using myN = N<decltype(std::bind(&base::f, std::declval<const base*>()))>;
auto boundMethod = std::bind(&base::f, static_cast<const base*>(this));


// Or both
using myN = N<decltype(std::bind(&base::f, std::declval<const base&>()))>;
auto boundMethod = std::bind(&base::f, static_cast<const base&>(*this));
Artyer
  • 31,034
  • 3
  • 47
  • 75