简体   繁体   中英

Why does template argument deduction failed with variadic template parameters of a std::function callback?

Let's consider the following functions:

// run_cb_1(): Explicitly defined prototype
void run_cb_1(const std::function<void(int)> & callback, int p)
{
    callback(p);
}

// run_cb_2(): One template parameter
template <typename T>
void run_cb_2(const std::function<void(T)> & callback, const T & t)
{
    callback(t);
}

// run_cb_3(): Variable number of template parameters
template <typename ... Args>
void run_cb_3(const std::function<void(Args...)> & callback, const Args & ... args)
{
    callback(args...);
}

Now if I want to use these functions as follows:

int main()
{
    auto f = [](int a){
        std::cout << a << '\n';
    };

    run_cb_1(f, 5);      // OK

    run_cb_2(f, 5);      // KO --> I understand why
    run_cb_2<int>(f, 5); // OK

    run_cb_3(f, 5);      // KO --> I understand why
    run_cb_3<int>(f, 5); // KO --> I don't understand why...

    return 0;
}

I get a "no matching function call" with run_cb_2() and run_cb_3() while it works perfectly fine with run_cb_1() .

I think it behaves as expected because I did not provided the type for the template argument (since it can not be deduced trivially as it is for run_cb_1() ).

But specifying the template type solves the problem for run_cb_2() (as I would expect) but not for run_cb_3() .


I know I can solve it either by explicitly declaring f as:

std::function<void(int)> f = [](int a){
    std::cout << a << '\n';
};

or by passing f as:

run_cb_2(std::function<void(int)>(f), 5);
run_cb_3(std::function<void(int)>(f), 5);

My question is: Why does the template argument deduction fail with run_cb_3() (with variadic template parameters) even when explicitly specifying the template type(s)?

It is obvious that I missed something (maybe basic) but I don't know what it is.
Any help will be appreciated.

The reason this fails is because there isn't just one type the compiler can use. When you do

run_cb_2<int>(f, 5);

The compiler looks at run_cb_2 and sees that there is only one template parameter. Since you've provided that it skips the deduction phase and stamps out run_cb_2<int> .

With

run_cb_3<int>(f, 5);

You're in a different boat. run_cb_3 has a variadic template parameter which means just supplying int is not enough to skip the deduction. You specified the first argument, but there could be more so it goes into the argument deduction phase to figure it out. That means it checks callback to make sure what it deduces there matches what it deduces for args . Since the lambda is not a std::function it can't deduce Args... from it. Once that happens the compiler stops and issues an error.

With run_cb_3<int> , you don't explicitly provide full Args... , just the first type; it might have other.

It is used for example in function such as std::make_unique :

template <class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args);

and

std::make_unique<MyObj>(var1, var2); // T = MyObj
                                     // Args... = [decltype((var1)), decltype((var2))]

Extra args are deduced from argument.

To force evaluation, you might use:

(&run_cb_3<int>)(f, 5); // OK

Demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM