Continue with the continuation of the monad-tuple. What's wrong?

Following the tuple continuation monad , let's say I define a functor std_tuple

to go from the category of tuple monads to std::tuple

:

auto std_tuple = [](auto... args)
{
    return [=](auto f){ return f(std::make_tuple(args...)); };
};

      

We can now use tuple monads in contexts expecting std::tuple

:

template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}

int main()
{
    tuple(1,2,3)(std_tuple)(f);
}

      

So far so good. Also, it doesn't compile. Clang 3.4.1 complains:

note: template-candidate ignored: failed to output template argument '$ auto-1-0'

in a call f(t)

inside a functor std_tuple

.

Is this correct, are these template arguments not being deduced? If so, why?

+3


source to share


1 answer


A simple case that reproduces your problem:

void f(int) {}
void f(double) {}

template<class T> void call_with_3( T t ) { t(3); }

int main() {
  call_with_3( f );
}

      

Here we can see that the call f

cannot be defined at the point where we pass it to call_with_3

. Now you don't seem to have many overloads (you only have one f

!), But ...

A is template

not an instance. A function template

is a factory of functions, not a function.

There is no object or meaning here.

When you pass the function name as an argument, overload resolution appears. If the target type is known (as a function reference or pointer), it is used to resolve the function name overload.

In this case, you are passing the function name to the template

( auto

) argument , so there is no overload resolution, so no specific value can be found, so you will get an error.

You can create an object whose purpose is to perform overload resolution on callable arguments with a given function name. I call them overloaded objects.

static struct f_overload_set_t {
  template<class... Args>
  auto operator()(Args&&... args) const {
    return f(std::forward<Args>(args)...);
  }
} f_overload_set;

      



in C ++ 11 you will need ->decltype( f( std::declval<Args>()... ) )

after const

.

Now f_overload_set(blah)

will, when called will (almost) do what happens when you f(blah)

, but f_overload_set

is the actual object. Therefore, you can transfer it.

Macros that generate such overload blocks are relatively easy to write. They can use lambdas as well, since the above is a lot like a stateless lambda if you think about it.

The good thing about a lambda based macro overload generator doesn't exist as it can be instantiated at the point of use. From @dyp's comment above:

#define OVERLOAD_SET( FUNC )\
  ([](auto&&... args){\
    return FUNC(std::forward<decltype(args)>(args)...);\
  })

      

(note: no parentheses around FUNC

as this blocks ADL). Parentheses around everything else, since otherwise, if used in a substring ( operator[]

) operation , it will be parsed as a [[

starting attribute, among other places (thanks to @ecatmur))

which your code does:

template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}

int main() {
  tuple(1,2,3)(std_tuple)(OVERLOAD_SET(f));
}

      

+4


source







All Articles