Как использовать 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); }));
Другие вопросы по тегам