Как заставить шаблон функции получать типы параметров своего лямбда-аргумента?

#c

#c

Вопрос:

Предположим, у нас есть шаблон класса, подобный этому:

 template <typename baseT, typename ...argTs>
class CCallable : public baseT
{
public:
    CCallable(std::function<bool(argTs...)> lambda)
    : m_lambda(lambda) {
    }
    bool Invoke(argTs ...args) override {
        return m_lambda(args...);
    }
private:
    std::function<bool(argTs...)> m_lambda;
};
 

И предположим, что у нас callback реализован шаблон функции, вероятно, похожий на этот псевдокод:

 template <typename baseT, typename lambdaT>
CCallable<baseT, typename lambdaT::argTs...> callback(lambdaT lambda)
{
    return CCallable<baseT, typename lambdaT::argTs...>(lambda);
}
 

чтобы мы могли это сделать:

 autoamp;amp; functor = callback<CBase>([](int x, int y, int *sum)->bool{
    *sum = x   y;
    return true;
});
// Start passing functor around and then...
int sum;
functor.Invoke(7, 42, amp;sum);
 

Пожалуйста, обратите внимание, что типы параметров лямбда-выражения не передаются в callback качестве аргументов его типа шаблона.

Как мы можем реализовать шаблон функции, подобный этому, чтобы избавить пользователей от ввода большего количества кодов, таких как:

 autoamp;amp; functor = callback<CBase, int, int, int*>([](int x, int y, int *sum)->bool{
    *sum = x   y;
    return true;
});
 

Спасибо.

Кстати, почему я спрашиваю об этом, потому Microsoft::WRL что предоставляет аналогичный шаблон с именем Callback , который вызывается много раз в библиотеке с открытым исходным кодом, которую я хочу использовать. Однако я предпочитаю создавать библиотеку с помощью GNU C вместо Visual C . Поэтому кажется неизбежным, что мне придется самому реализовать Microsoft::WRL::Callback подобный шаблон или макрос.

Комментарии:

1. Помните, что лямбда-выражение может вызываться с несколькими типами… (Например, автоматический параметр im c 14)

2. В документе это выглядит так, что необходимо указать параметр шаблона, нет?

3. Вы могли бы создать Invoke() сам шаблон.

4. @user202729 У него должен быть аргумент типа шаблона, но единственным обязательным параметром типа является базовый класс, а не какие-либо типы в списке параметров лямбда, которому передается Callback . Возможно Callbak , он получает эти типы из базового класса idk.

5. @G.Sliepen Нет, шаблону метода не разрешается быть виртуальным. Смотрите ключевое override слово там после списка параметров Invoke ?

Ответ №1:

С C 17 CTAD вы можете использовать руководства по вычету для std::function :

 template<class T>
struct Identity { };

template<typename baseT, typename ...argTs>
class CCallable : public baseT {
public:
    CCallable(std::function<bool(argTs...)> lambda, Identity<baseT> = {})
        : m_lambda(lambda) {
    }

    bool Invoke(argTs ...args) override {
        return m_lambda(args...);
    }

private:
    std::function<bool(argTs...)> m_lambda;
};

template<typename baseT, typename lambdaT>
auto callback(lambdaT lambda) {
    return CCallable(std::function(lambda), Identity<baseT>{});
}
 

CTAD — это все или ничего, поэтому тип baseT заключен в Identity , чтобы сделать его выводимым.

ДЕМОНСТРАЦИЯ

Комментарии:

1. Я думаю, что на земле, отличной от C 17, это в основном так template<typename B, typename F, typename G, typename R, typename... As> CCallback<B, As...> callback_impl(F amp;f, R (G::*)(As...)) { return CCallable<B, As...>(f); } template<typename B, typename F> auto callback(F f) /* -> decltype(callback_impl<B>(f, amp;F::operator())) */ { return callback_impl<B>(f, amp;F::operator()); } (в этом нет необходимости Identity ). Я на самом деле не понимал, что стандартная библиотека санкционировала это! Я всегда думал, что это немного взлом,

2. @HTNW Спасибо. Ваш код работает отлично, даже если аргументом callback является лямбда, а std::function<> не объект. AFAIK, компиляторы не выполняют неявное преобразование для аргументов шаблонов функций, но в этом случае похоже, что лямбда неявно преобразуется в a std::function<> . Почему? Что произошло? Я в замешательстве.

3. @Cody Неявное преобразование подавляется только во время вывода аргумента шаблона, потому что мы можем не знать тип, в который нам нужно преобразовать (поскольку мы еще не вывели параметры шаблона). Если мы задали достаточно аргументов шаблона, чтобы узнать тип параметра, неявное преобразование может продолжаться как обычно.

4. @HTNW Позвольте мне прояснить это для людей, которые, возможно, ищут решение той же проблемы. Если использовать nm или objdump для проверки символов, сгенерированных 2 шаблонами функций, упомянутыми выше, нет никаких доказательств того, что при передаче аргументов в шаблоны функций происходило неявное преобразование. Лямбда-выражение по-прежнему остается лямбдой. std::function<> Объект создается callback_impl после callback_impl получения типов аргументов лямбда-аргумента из callback .