Title: How to use CTAD for a lambda?
Tags: c++;templates;c++17;ctad
Question: Good afternoon, everyone. I implemented a couple of classes:
// CallingDelegate
template <typename Result, typename ... Args&gt;
class CallingDelegate
using TypeDelegate = st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<Result(Args...)&gt;;
public:
CallingDelegate() = delete;
CallingDelegate(const st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0shared_ptr<TypeDelegate&gt;&amp; boxDelegate) : m_boxDelegate(boxDelegate) {}
public:
Result operator()(Args... args) const
if (m_boxDelegate)
{
const auto&amp; delegate = (*m_boxDelegate.get());
if (delegate)
{
return delegate(st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0forward<Args&gt;(args)...);
}
}
return {};
private:
const st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0shared_ptr<TypeDelegate&gt; m_boxDelegate;
// HolderDelegate
template <typename Result, typename ... Args&gt;
class HolderDelegate
using TypeDelegate = st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<Result(Args...)&gt;;
public:
using TypeCalling  = CallingDelegate<Result, Args...&gt;;
public:
~HolderDelegate() { (*m_boxDelegate.get()) = nullptr; }
HolderDelegate() = delete;
HolderDelegate(const TypeDelegate&amp; delegate) : m_boxDelegate(st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0make_shared<TypeDelegate&gt;    (delegate)) {}
//
template<typename TypeCallback&gt;
HolderDelegate(const TypeCallback&amp; callback) : m_boxDelegate(st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0make_shared<TypeDelegate&gt;    (TypeDelegate(callback))) {}
public: /// NON COPY
HolderDelegate(const HolderDelegate&amp;) = delete;
HolderDelegate&amp; operator=(const HolderDelegate&amp; other) = delete;
public:
inline TypeCalling getCalling() const { return TypeCalling(m_boxDelegate); }
private:
const st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0shared_ptr<TypeDelegate&gt; m_boxDelegate;
you can use it like this:
// Func
void runDelegate(const st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(size_t)&gt; delegate)
for (size_t i = 0; i < 3; ++i)
printf(&quot;index: %zu =&gt; text: %s\n&quot;, i, delegate(i).c_str());
// Main
int main()
st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(size_t)&gt; delegate;
const HolderDelegate<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string, size_t&gt; holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
delegate = holder.getCalling();
runDelegate(delegate);
runDelegate(delegate);
return 0;
In the 17 standard, you can use the following:
// Main
int main()
st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(size_t)&gt; delegate;
const st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(size_t)&gt; delegateRaw = [] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); };
const HolderDelegate holder (delegateRaw);
delegate = holder.getCalling();
runDelegate(delegate);
runDelegate(delegate);
return 0;
But you can't use it like this (using lambda):
// Main
int main()
st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(size_t)&gt; delegate;
const HolderDelegate holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
delegate = holder.getCalling();
runDelegate(delegate);
runDelegate(delegate);
return 0;
Maybe I missed the standard information somewhere, or maybe it can't be implemented. Tell me please. I will be glad of any information.
Error:
main.cpp: In function 'int main()':
main.cpp:65:104: error: class template argument deduction     failed:
const HolderDelegate holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
^
main.cpp:65:104: error: no matching function for call to     'HolderDelegate(main()75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0<lambda(size_t)&gt;)'
main.cpp:44:2: note: candidate: 'template<class Result, class     ... Args&gt; HolderDelegate(const HolderDelegate<Result, Args&gt;&amp;)-&gt;     HolderDelegate<Result, Args&gt;'
HolderDelegate(const HolderDelegate&amp;) = delete;
^~~~~~~~~~~~~~
main.cpp:44:2: note:   template argument deduction/substitution     failed:
main.cpp:65:104: note:   'main()75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0<lambda(size_t)&gt;' is not     derived from 'const HolderDelegate<Result, Args&gt;'
const HolderDelegate holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
^
main.cpp:42:2: note: candidate: 'template<class Result, class     ... Args, class TypeCallback&gt; HolderDelegate(const TypeCallback&amp;)-&gt;     HolderDelegate<Result, Args&gt;'
HolderDelegate(const TypeCallback&amp; callback) :     m_boxDelegate(st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0make_shared<TypeDelegate&gt;(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) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
^
main.cpp:39:2: note: candidate: 'template<class Result, class     ... Args&gt; HolderDelegate(const TypeDelegate&amp;)-&gt; HolderDelegate<Result, Args&gt;'
HolderDelegate(const TypeDelegate&amp; delegate) :     m_boxDelegate(st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0make_shared<TypeDelegate&gt;(delegate)) {}
^~~~~~~~~~~~~~
main.cpp:39:2: note:   template argument deduction/substitution     failed:
main.cpp:65:104: note:   'main()75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0<lambda(size_t)&gt;' is not     derived from 'const TypeDelegate'
const HolderDelegate holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
^
main.cpp:38:2: note: candidate: 'template<class Result, class     ... Args&gt; HolderDelegate()-&gt; HolderDelegate<Result, Args&gt;'
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) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
^
main.cpp:31:7: note: candidate: 'template<class Result, class     ... Args&gt; HolderDelegate(HolderDelegate<Result, Args&gt;)-&gt; HolderDelegate<Result,     Args&gt;'
class HolderDelegate
^~~~~~~~~~~~~~
main.cpp:31:7: note:   template argument deduction/substitution     failed:
main.cpp:65:104: note:   'main()75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0<lambda(size_t)&gt;' is not     derived from 'HolderDelegate<Result, Args&gt;'
const HolderDelegate holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
^
mingw32-make[1]: *** [Makefile.Debug:105: debug/main.o] Error 1
Comment: Does this answer your question? [C++11 does not deduce type when st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function or lambda functions are involved](https://stackoverflow.com/questions/9998402/c11-does-not-deduce-type-when-stdfunction-or-lambda-functions-are-involved)

Comment: @Fureeish I think not

Comment: what does it mean that you "can't" ? Please include the compiler error in the question

Comment: Can you provide a [mcve]? There's a lot of code here, and nearly all of it I suspect is unrelated to the question. Also "can't use it like this" could use some elaboration on why you can't... what's the problem?


Here is another answer: If your lambda doesn't capture anything, then you can decay it to a function pointer at the call site:
const HolderDelegate holder ( + [] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string { return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
// ^ decay to FP
and then you can add a deduction guide for HolderDelegate:
template <typename Result, typename ... Args&gt;
HolderDelegate(Result (*)(Args...)) -&gt; HolderDelegate<Result, Args...&gt;;
Here's a demo.
Comment for this answer: Thanks. Can't you do that for any lambda?
Comment for this answer: No, this solution will only work for capture-less lambdas.
Comment for this answer: It's worth mentioning that this will only work if the types are explicitly specified in the parameter list (e.g. no `auto` parameters) -- otherwise it can't deduce specific argument types

Here is another answer: It's complicated...
const st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(size_t)&gt; delegateRaw = [] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); };
const HolderDelegate holder (delegateRaw);
you pass a st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0size_t)&gt; to the HolederDelegate constructor that has a constructor waiting for a st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function.
So the new (C++17) automatic deduction guides detect HolderDelegates as HolderDelegates<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string, st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0size_t&gt;.
But if you pass a lambda function to the HolderDelegates constructor
const HolderDelegate holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
you have a sort of chicken-and-egg problem because the lambda can be converted to a st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function but isn't a st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function.
So the types of the st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function can't be deduced and the automatic deduction guides doesn't works.
To go around this problem I see tree possible solutions.
The first one is your working way
const st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string(size_t)&gt; delegateRaw = [] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); };
const HolderDelegate holder (delegateRaw);
The second one is explicit the types of HolderDelegates so no deduction is needed at all
// .................VVVVVVVVVVVVVVVVVVVVVVVvvv
const HolderDelegate<st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string, st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0size_t&gt; holder ([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); });
The third one is involve the deduction guides for st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function,
// ..........................VVVVVVVVVVVVVV...........................................................................-V
const HolderDelegate holder (st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function([] (const size_t index) -&gt; st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0string {     return st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0to_string(index); }));
Comment for this answer: And how does st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function deduce the type of lambda?
Comment for this answer: @ОлегПривалов - Your idea is replicate the `st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function`'s deduction guides for `HolderDelegate`? Intriguing idea but, unfortunately, I don't know how they works (except for the function-pointer case, and you can see good example in the cigien's answer). Given that `st75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0function` is a very flexible answer, I suppose they are very complex but it's only a supposition.

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 4, 'ContentLicense': 'CC BY-SA 4.0', 'CreationDate': '2020:00:08/31/20', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<c++><templates><c++17><ctad>', 'Title': 'How to use CTAD for a lambda?', 'ViewCount': 199, 'id': 63673322, 'user_AboutMe': None, 'user_AccountId': 14791363.0, 'user_DisplayName': 'Олег Привалов', 'user_Reputation': 177.0, 'user_Views': 43.0, 'user_id': 10682199.0}}