Reputation: 26943
I'm trying to understand why this snippet fails:
#include <iostream>
using namespace std;
template <typename Lambda>
struct Handler
{
bool _isCompleted;
bool isCompleted() { return _isCompleted; }
Lambda _l;
Handler(Lambda&& l) : _l(l) {}
void call() { _l(this); }
};
int main()
{
auto l1 = new Handler( [&](decltype(l1) obj )->
{
obj->_isCompleted = true;
cout << " is completed?" << obj->isCompleted() << endl;
});
l1->call();
};
g++ 4.5 fails with:
test.cpp: In function ‘int main()’:
test.cpp:21:17: error: expected type-specifier before ‘Handler’
test.cpp:21:17: error: expected ‘,’ or ‘;’ before ‘Handler’
test.cpp:25:2: error: expected primary-expression before ‘)’ token
test.cpp:25:2: error: expected ‘;’ before ‘)’ token
test.cpp:26:7: error: request for member ‘call’ in ‘* l1’, which is of non-class type ‘int’
my understanding is that auto l1
should resolve to Handler<lambdaType>*
and lambdaType should have a public function signature void( Handler<LambdaType>*)
. I don't see any blatantly wrong with the above example (you know, besides the ugliness and the slightly pathological cyclic dependency between the lambda and the handler type)
Upvotes: 1
Views: 666
Reputation: 131789
One problem is, as @Cat said, that template argument deduction just does not work for constructor calls. You always need to specify the template argument.
The other problem is nicely illustrated by Clang with the following snippet:
struct X{
X(...){}
};
int main(){
auto l = X([](decltype(l)& o){});
}
Output:
t.cpp:6:26: error: variable 'l' declared with 'auto' type cannot appear in its
own initializer
auto l = X([](decltype(l)& o){});
^
1 error generated.
Obligatory standard quote:
§7.1.6.4 [dcl.spec.auto] p3
Otherwise, the type of the variable is deduced from its initializer. The name of the variable being declared shall not appear in the initializer expression. [...]
Upvotes: 5
Reputation: 129764
Type deduction doesn't work for constructors. auto
will deduce the type for the expression, yes, but new Handler()
requires explicit type. Write a factory function instead:
// also don't use raw owning pointers
template <typename L>
std::unique_ptr<Handler<L>> make_handler(L lambda) {
return std::unique_ptr<Handler<L>>(new Handler<L>(lambda));
}
Sure, it's repeating yourself a bit, but only once. Then you can do
auto l1 = make_handler([](...) { ... });
auto l2 = make_handler([](...) { ... });
and it'll work fine.
Upvotes: 2