Как расширить пакет параметров шаблона функции в std::function в качестве аргумента функции?

Этот вопрос является продолжением другого вопроса десятилетней давности:

#include<functional>
template <typename ReturnType, typename... ArgumentTypes>
struct Caller
{
    static void Call(std::function<ReturnType(ArgumentTypes...)>);
};
template <typename ReturnType, typename...ArgumentTypes>
void Call(std::function<ReturnType(ArgumentTypes...)>f);
int main()
{
    Caller<void>::Call([]() {});//Well-formed
    Call<void>({[]() {}});//Well-formed
    Call<void>([]() {});//Ill-formed
}

Код предоставляет три метода для расширения пакета параметров шаблона в сигнатуре std::function. Первые два были протестированы как правильно сформированные и могут быть скомпилированы; последний такой же, как и старый вопрос, и поэтому имеет неправильную форму. Вопрос в том, каким образом первые два метода обходят проблему, вызывающую сбой компиляции в исходной задаче и последнем методе?

Я не могу сказать, почему Call<void>({[]() {}});//Well-formed компилируется, а Call<void>([]() {});//Ill-formed нет, потому что: stackoverflow.com/questions/53326206/…

NathanOliver 26.08.2024 16:48

Для структуры Caller передаются все аргументы шаблона (только void и пустой пакет), лямбду можно преобразовать в std::function<void()>.

Jarod42 26.08.2024 17:01

@NathanOliver - предоставленная вами ссылка основана на том, что «лямбда-выражение не выводит параметры шаблона std::function, поскольку они являются несвязанными типами», что неверно. std::function может выводить типы возвращаемых значений и аргументов из лямбды, если они не являются шаблонами (auto)

Gene 26.08.2024 18:43

@Gene Это можно сделать с помощью std::function foo = lambda из-за CTAD. Это не работает, если параметры std::function выводятся из параметра функции: coliru.stacked-crooked.com/a/4aceb8468800936c

NathanOliver 26.08.2024 18:50

@NathanOliver - Я не говорю, что вывод в этом конкретном случае не должен быть ошибочным, я говорю, что ответ в этой ссылке неверен.

Gene 26.08.2024 18:55

@ Джин, мне придется с тобой не согласиться.

NathanOliver 26.08.2024 18:56
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для структуры Caller передаются все аргументы шаблона (только void и пустой пакет), поскольку частичных CTAD нет. Лямбду можно преобразовать в std::function<void()>, так что всё в порядке.

Для шаблона функции Call предоставленные аргументы являются только первыми, вычет все равно может произойти.

Один из способов остановить возможный вывод — сохранить в промежуточной переменной:

auto call = Call<void>; // void (*)()
call([](){}); // OK

Демо

лямбда не являются std::function, поэтому их нельзя вывести как std::function<Ret(Args...)>, поэтому третий метод не работает.

За второе дополнительная {..} также останавливает вычет: См. template_argument_deduction#Non-deduced_contexts #7 для более подробной информации:

Параметр P, где A представляет собой список инициализации в скобках, но P не является std::initializer_list, ссылку на него (возможно, с указанием cv) или ссылку на массив}}:

Поскольку {..} не имеет типа, не должен ли вывод типа для Args... потерпеть неудачу?

NathanOliver 26.08.2024 17:29

Изначально я думал то же самое о случае {..}, но тогда он работает только с пустым пакетом параметров. Если вы измените аргумент на int, Call<void>({[](int) {}}); потерпит неудачу, а Call<void>(std::function([](int) {})); увенчается успехом.

Gene 26.08.2024 18:30

Я думаю, что причина наблюдаемого поведения заключается в том, что во всех этих случаях аргументы функции не выводятся. Явно специализированный Call<void> раскрывается как имеющий пустой пакет, что означает, что совпадать может только int(void) лямбда. При использовании std::function приведения выводятся как тип возвращаемого значения, так и пакеты параметров.

Gene 26.08.2024 18:59

Поскольку объяснение второго случая из этого ответа вычеркнуто, какое объяснение является правильным?

埃博拉酱 27.08.2024 13:38

@埃博拉酱: Мне пришлось немного разобраться, чтобы найти правильный ответ.

Jarod42 27.08.2024 14:09

Другие вопросы по теме