-1

When I attempt to call the method target on a std::function object using a template parameter pack, the compiler throws an error, but it works fine if the template parameters are specified explicitly in a proxy variable.

Example, modified from cppreference.com:

#include <functional>
#include <iostream>

int f(int, int) { return 1; }
int g(int, int) { return 2; }

template <typename... Args>
void test(std::function<int(Args...)> const& arg)
{
    auto && ptr = arg.target<int(*)(Args...)>(); // error: expected primary-expression before 'int'

    if (ptr && *ptr == f)
        std::cout << "it is the function f\n";
    if (ptr && *ptr == g)
        std::cout << "it is the function g\n";
}

int test()
{
    test<int, int>(std::function<int(int, int)>(f));
    test<int, int>(std::function<int(int, int)>(g));
}

I've tried various other ways to call target including the following:

int (*const* ptr)(Args...) = arg.target<int(*)(Args...)>(); // error: expected primary-expression before 'int'
int (*const* ptr)(int, int) = arg.target<int(*)(Args...)>(); // error: expected primary-expression before 'int'
int (*const* ptr)(int, int) = arg.target<int(*)(int, int)>(); // error: expected primary-expression before 'int'

const std::function<int(Args...)>& func = arg;
auto && ptr = arg.target<int(*)(Args...)>(); // error: expected primary-expression before 'int'

The compiler only appeared to be satisfied when using a proxy variable as seen here:

const std::function<int(int, int)>& func = arg;
auto && ptr = func.target<int(*)(Args...)>(); // OK, but requires specifying the template arguments for func

As can be seen, this workaround deviates from the previous non-working example only by explicitly specifying the function parameters instead of relying on the parameter pack. (I could bypass the proxy variable with a static_cast e.g. static_cast<std::function<int(int,int)>&>(arg).target<int(*)(Args...)>(), but this also requires knowing the parameters for the cast.) If I already knew what parameters were going to be used then I wouldn't be using a variadic template since it wouldn't be necessary. But since I don't, how can I fix this? Am I doing something wrong, is this a compiler bug, or what exactly is going on here?

Note:

For reference, I'm compiling with GCC version 6.3.0 with MinGW on Windows 10.

Chris
  • 325
  • 1
  • 3
  • 11

1 Answers1

1
arg.template target<int(*)(Args...)>(

You need to disambiguate when the std function type is dependent on template args.

C++ doesn't know arg.target is a template when parsing your template function prior to the function being called. And it has to parse it before it is called; this isn't a macro.

So whenever you name a type in a context dependent on your template arguments, or instantiate a template in a context dependent on your template args, you have to tell the parser.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thank you! I'd forgotten disambiguation was a thing. I really should learn how the compiler parses these things but they're always just convoluted enough for me to rather stick them in a black box. – Chris Jan 12 '21 at 03:53