Как использовать CTAD для лямбды?
Добрый день всем. Я реализовал пару классов:
// CallingDelegate
template <typename Result, typename ... Args>
class CallingDelegate
{
using TypeDelegate = std::function<Result(Args...)>;
public:
CallingDelegate() = delete;
CallingDelegate(const std::shared_ptr<TypeDelegate>& boxDelegate) : m_boxDelegate(boxDelegate) {}
public:
Result operator()(Args... args) const
{
if (m_boxDelegate)
{
const auto& delegate = (*m_boxDelegate.get());
if (delegate)
{
return delegate(std::forward<Args>(args)...);
}
}
return {};
}
private:
const std::shared_ptr<TypeDelegate> m_boxDelegate;
};
// HolderDelegate
template <typename Result, typename ... Args>
class HolderDelegate
{
using TypeDelegate = std::function<Result(Args...)>;
public:
using TypeCalling = CallingDelegate<Result, Args...>;
public:
~HolderDelegate() { (*m_boxDelegate.get()) = nullptr; }
HolderDelegate() = delete;
HolderDelegate(const TypeDelegate& delegate) : m_boxDelegate(std::make_shared<TypeDelegate> (delegate)) {}
//
template<typename TypeCallback>
HolderDelegate(const TypeCallback& callback) : m_boxDelegate(std::make_shared<TypeDelegate> (TypeDelegate(callback))) {}
public: /// NON COPY
HolderDelegate(const HolderDelegate&) = delete;
HolderDelegate& operator=(const HolderDelegate& other) = delete;
public:
inline TypeCalling getCalling() const { return TypeCalling(m_boxDelegate); }
private:
const std::shared_ptr<TypeDelegate> m_boxDelegate;
};
вы можете использовать это так:
// Func
void runDelegate(const std::function<std::string(size_t)> delegate)
{
for (size_t i = 0; i < 3; ++i)
{
printf("index: %zu => text: %s\n", i, delegate(i).c_str());
}
}
// Main
int main()
{
std::function<std::string(size_t)> delegate;
{
const HolderDelegate<std::string, size_t> holder ([] (const size_t index) -> std::string { return std::to_string(index); });
delegate = holder.getCalling();
runDelegate(delegate);
}
runDelegate(delegate);
return 0;
}
В стандарте 17 можно использовать следующее:
// Main
int main()
{
std::function<std::string(size_t)> delegate;
{
const std::function<std::string(size_t)> delegateRaw = [] (const size_t index) -> std::string { return std::to_string(index); };
const HolderDelegate holder (delegateRaw);
delegate = holder.getCalling();
runDelegate(delegate);
}
runDelegate(delegate);
return 0;
}
Но вы не можете использовать это так (используя лямбда):
// Main
int main()
{
std::function<std::string(size_t)> delegate;
{
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
delegate = holder.getCalling();
runDelegate(delegate);
}
runDelegate(delegate);
return 0;
}
Может я где-то упустил стандартную информацию, а может это нереализуемо. Скажи мне, пожалуйста. Буду рад любой информации.
Ошибка:
main.cpp: In function 'int main()':
main.cpp:65:104: error: class template argument deduction failed:
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
^
main.cpp:65:104: error: no matching function for call to 'HolderDelegate(main()::<lambda(size_t)>)'
main.cpp:44:2: note: candidate: 'template<class Result, class ... Args> HolderDelegate(const HolderDelegate<Result, Args>&)-> HolderDelegate<Result, Args>'
HolderDelegate(const HolderDelegate&) = delete;
^~~~~~~~~~~~~~
main.cpp:44:2: note: template argument deduction/substitution failed:
main.cpp:65:104: note: 'main()::<lambda(size_t)>' is not derived from 'const HolderDelegate<Result, Args>'
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
^
main.cpp:42:2: note: candidate: 'template<class Result, class ... Args, class TypeCallback> HolderDelegate(const TypeCallback&)-> HolderDelegate<Result, Args>'
HolderDelegate(const TypeCallback& callback) : m_boxDelegate(std::make_shared<TypeDelegate>(TypeDelegate(callback))) {}
^~~~~~~~~~~~~~
main.cpp:42:2: note: template argument deduction/substitution failed:
main.cpp:65:104: note: couldn't deduce template parameter 'Result'
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
^
main.cpp:39:2: note: candidate: 'template<class Result, class ... Args> HolderDelegate(const TypeDelegate&)-> HolderDelegate<Result, Args>'
HolderDelegate(const TypeDelegate& delegate) : m_boxDelegate(std::make_shared<TypeDelegate>(delegate)) {}
^~~~~~~~~~~~~~
main.cpp:39:2: note: template argument deduction/substitution failed:
main.cpp:65:104: note: 'main()::<lambda(size_t)>' is not derived from 'const TypeDelegate'
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
^
main.cpp:38:2: note: candidate: 'template<class Result, class ... Args> HolderDelegate()-> HolderDelegate<Result, Args>'
HolderDelegate() = delete;
^~~~~~~~~~~~~~
main.cpp:38:2: note: template argument deduction/substitution failed:
main.cpp:65:104: note: candidate expects 0 arguments, 1 provided
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
^
main.cpp:31:7: note: candidate: 'template<class Result, class ... Args> HolderDelegate(HolderDelegate<Result, Args>)-> HolderDelegate<Result, Args>'
class HolderDelegate
^~~~~~~~~~~~~~
main.cpp:31:7: note: template argument deduction/substitution failed:
main.cpp:65:104: note: 'main()::<lambda(size_t)>' is not derived from 'HolderDelegate<Result, Args>'
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
^
mingw32-make[1]: *** [Makefile.Debug:105: debug/main.o] Error 1
2 ответа
Если ваша лямбда ничего не захватывает, вы можете преобразовать ее в указатель функции на сайте вызова:
const HolderDelegate holder ( + [] (const size_t index) -> std::string { return std::to_string(index); });
// ^ decay to FP
а затем вы можете добавить руководство по вычету для HolderDelegate
:
template <typename Result, typename ... Args>
HolderDelegate(Result (*)(Args...)) -> HolderDelegate<Result, Args...>;
Вот демо.
Это трудно...
С участием
const std::function<std::string(size_t)> delegateRaw = [] (const size_t index) -> std::string { return std::to_string(index); };
const HolderDelegate holder (delegateRaw);
вы проходите std::function<std::string(std::size_t)>
к HolederDelegate
конструктор, у которого есть конструктор, ожидающий std::function
.
Итак, новые (C++17) руководства автоматического вывода обнаруживают HolderDelegates
в качестве HolderDelegates<std::string, std::size_t>
.
Но если вы передадите лямбда-функцию в HolderDelegates
конструктор
const HolderDelegate holder ([] (const size_t index) -> std::string { return std::to_string(index); });
у вас есть своего рода проблема с курицей и яйцом, потому что лямбда может быть преобразована в std::function
но неstd::function
.
Итак, типы std::function
не может быть выведен, и руководство по автоматическому вычету не работает.
Чтобы обойти эту проблему, я вижу дерево возможных решений.
Первый - ваш рабочий путь
const std::function<std::string(size_t)> delegateRaw = [] (const size_t index) -> std::string { return std::to_string(index); };
const HolderDelegate holder (delegateRaw);
Второй - явные типы HolderDelegates
так что никакого вычета не требуется
// .................VVVVVVVVVVVVVVVVVVVVVVVvvv
const HolderDelegate<std::string, std::size_t> holder ([] (const size_t index) -> std::string { return std::to_string(index); });
Третий - это руководство по дедукции для std::function
,
// ..........................VVVVVVVVVVVVVV...........................................................................-V
const HolderDelegate holder (std::function([] (const size_t index) -> std::string { return std::to_string(index); }));