У меня есть простой класс, в котором хранится вызываемый объект (либо лямбда-функция, либо глобальная функция) и void*, оба из которых передаются при построении:
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
template <typename>
class LyricAnatali;
template <typename Res, typename... Args>
class LyricAnatali<Res(Args...)> {
public:
template <typename F>
LyricAnatali(F&& f, void* data)
: m_ptr(f), m_userData(data) {}
private:
Res(*m_ptr)(void*, Args...) = nullptr;
void* m_userData;
};
// Deduction guide
template <typename Res, typename... Args>
LyricAnatali(Res(*)(void*, Args...), void*) -> LyricAnatali<Res(Args...)>;
int GlobalFunction(void*, const string& s, const vector<int>& t, int u) { return 0; }
int main() {
auto lg = [](void*, const string& s, const vector<int>& t, int u) -> int { return 0; };
LyricAnatali<int(const string& s, const vector<int>& t, int u)> func(lg, 0); // Compiles
auto lambda = LyricAnatali(lg, 0); // Does not compile
auto global = LyricAnatali(GlobalFunction, 0); // Compiles
auto stdFunc = std::function(lg); // Compiles
}
Указанная выше строка не компилируется. Из https://godbolt.org/z/7K3K48Tn4 я чувствую, что соответствующая ошибка:
could not match 'Res (*)(void *, Args...)' against '(lambda at <source>:28:13)'
LyricAnatali(Res(*)(void*, Args...), void*) -> LyricAnatali<Res(Args...)>;
Я недостаточно понимаю в руководствах по дедукции, чтобы знать, как это исправить. Может кто-нибудь посоветовать, как я могу заставить это работать при передаче лямбды?
Имя лямбды не распадается на указатель функции, как это делает имя функции. Лямбда-выражение приводит к объекту замыкания, который является функтором, классом, который действует как функция.
Несколько распространенным сокращением для принудительного преобразования лямбды в указатель на функцию является применение к ней унарной operator+
, т. е. auto lambda = LyricAnatali(+lg, 0);
будет компилироваться. Достаточно ли это ясно или слишком умно для использования в реальной кодовой базе, решать вам.
lambda — это тип класса с operator()
(в данном случае const
), и он не будет соответствовать указателю на функцию, для этого вам нужно написать руководство по дедукции. (это общее правило вывода шаблонов, не ограничивающееся CTAD)
возможная реализация
template <typename...>
struct parse_member{};
template <typename R, typename G, typename... A>
struct parse_member<R(G::*)(void*, A...)const>{ // and all combination of const, noexcept, volatile, &, &&, for general case
using type = R(A...);
};
template <
typename F,
typename sig = typename parse_member<decltype(&F::operator())>::type
>
LyricAnatali(F, void*) -> LyricAnatali<sig>;
да руководство по дедукции не простое. en.cppreference.com/w/cpp/utility/functional/function/… (есть вызываемые объекты, функции-члены и возможные cv-квалификаторы)