7
#include <functional>
#include <iostream>
template<typename T>
class MaybePtr{
    T* ptr;

public:
    MaybePtr(T* p) : ptr(p) {}

    template <typename F,typename R = std::result_of<F(T*)>::type>
    R Get(F access,F default){
        if (ptr != nullptr)
            return access(ptr);
        else
            return default(ptr);   
    }
};
template <typename T>
void f_void(T*) {}
int main(){
    int * iptr = new int;
    *iptr = 10;
    auto m = MaybePtr<int>(iptr);
    auto f = [](int* i) -> int {return *i + 1; };
    auto f1 = [](int* i) -> int { return 0; };
    int r = m.Get(f, f1); // error C2782
    std::cout << f(iptr);
    int i;
    std::cin >> i;
}

Error

error C2782: 'R MaybePtr<int>::Get(F,F)' : template parameter 'F' is ambiguous

Why is F ambiguous? It should know that F is a function that takes a T* and returns a R.

Maik Klein
  • 15,548
  • 27
  • 101
  • 197

1 Answers1

12

A lambda with an empty closure may decay to a function pointer but here :

static_assert( std::is_same<decltype(f),decltype(f1)>::value,"different types" );

The error is normal, also Visual Studio is laxist, but you miss a typename and default is a reserved keyword.

template <typename F,typename R = typename std::result_of<F(T*)>::type>

A trick exists: force the lambda to decay to a function pointer. The below line compiles and does what you expect, of course, only valid with empty closure :

int r = m.Get(+f, +f1);
galop1n
  • 8,573
  • 22
  • 36
  • 1
    @MarcoA.: Had the same idea. Drawback: You loose the brevity given by `auto`. – Zeta Jul 28 '14 at 11:17
  • And it introduces `std::function`, a heavy object, adding indirection, possible copy, … – galop1n Jul 28 '14 at 11:18
  • 1
    @galop1n Can you describe a bit more that `+f` ? I do not understand what it is and what it does. – Johan Jul 28 '14 at 11:20
  • Could you explain why it could potentially copy something? – Maik Klein Jul 28 '14 at 11:22
  • @Mark it copy because it is state as it, ctor 3 and 4 copy or move to the function object member [http://en.cppreference.com/w/cpp/utility/functional/function/function]. Of course, the copy is limited in the case of a simple function pointer, but if it was a fat stateful functor, it may even add allocation as the std::function inline storage would not be enough. – galop1n Jul 28 '14 at 11:25
  • 2
    @Johan It basically tries to apply the + operator to something that can be only operated in this context via a pointer, as a stateless lambda can decay to a function _pointer_ it does that. "5.1.2.6 The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non- explicit const conversion function to pointer to function (...)" – Red XIII Jul 28 '14 at 12:01
  • In this context `+` is called the *decay operator*. – Potatoswatter Jul 28 '14 at 13:13
  • 1
    Just for the reference: "The operand of the unary + operator shall have arithmetic, unscoped enumeration, **or pointer type** and the result is the value of the argument." That's important as other unary operators don't have this property. – Red XIII Jul 28 '14 at 13:57
  • @Johan You might also like to have a look at [this](http://stackoverflow.com/questions/18889028) Q/A for `+f` :) – Daniel Frey Jul 28 '14 at 15:43