Почему вывод аргументов шаблона не удался с переменными параметрами шаблона обратного вызова std :: function? - PullRequest
1 голос
/ 15 октября 2019

Давайте рассмотрим следующие функции:

// 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...);
}

Теперь, если я хочу использовать эти функции следующим образом:

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;
}

Я получаю "нет соответствующего вызова функции" с run_cb_2() и run_cb_3(), в то время как он прекрасно работает с run_cb_1().

Я думаю, что он ведет себя так, как ожидалось, потому что я не предоставил тип для аргумента шаблона (так как он не может бытьвыводится тривиально, как для run_cb_1()).

Но указание типа шаблона решает проблему для run_cb_2() (как я и ожидал), но не для run_cb_3().


Я знаю, что могу решить это или , явно объявив f как:

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

или , передав f как:

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

У меня такой вопрос: Почему вывод аргумента шаблона завершается неудачно с run_cb_3() (с переменными параметрами шаблона) даже при явном указании типа (ов) шаблона?

ЭтоОчевидно, что я что-то упустил (может быть, основной), но я не знаю, что это такое. Любая помощь будет оценена.

Ответы [ 2 ]

1 голос
/ 15 октября 2019

Причина, по которой это не удается, заключается в том, что компилятор не может использовать только один тип. Когда вы делаете

run_cb_2<int>(f, 5);

Компилятор смотрит на run_cb_2 и видит, что есть только один параметр шаблона. Поскольку вы указали, что он пропускает фазу вычета и печатает run_cb_2<int>.

С

run_cb_3<int>(f, 5);

Вы находитесь в другой лодке. run_cb_3 имеет параметр шаблона переменной, который означает, что просто указать int недостаточно, чтобы пропустить вычет. Вы указали первый аргумент, но может быть и больше, поэтому он входит в фазу вывода аргументов, чтобы выяснить это. Это означает, что он проверяет callback, чтобы убедиться, что то, что он выводит, соответствует тому, что он выводит для args. Поскольку лямбда не является std::function, она не может вывести из нее Args.... Как только это происходит, компилятор останавливается и выдает ошибку.

0 голосов
/ 15 октября 2019

С run_cb_3<int> вы явно не предоставляете полный Args..., только первый тип;может иметь другое значение.

Используется, например, в таких функциях, как std::make_unique:

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

и

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

Дополнительные аргументы выводятся из аргумента.

Для принудительной оценки вы можете использовать:

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

Демо

...