I'm trying to hammer down the type of a this-capturing lambda using a special generating member function with decltype(auto). However, the compiler resists to determine the lambda's type for the following contexts that require it, even though it is declare before. How can I circumvent this problem?
EDIT: My overarching goal is to have a this-capturing lambda as a class member.
#include <cstdio>
#include <utility>
struct entity
{
auto gen_lambda() -> decltype(auto)
{
return [this](){
printf("Hello!\n");
};
}
entity()
: lambda_(gen_lambda())
{}
decltype(std::declval<entity>().gen_lambda()) lambda_;
};
int main()
{
entity e;
e.lambda_();
}
Yields:
<source>:17:47: error: use of 'decltype(auto) entity::gen_lambda()' before deduction of 'auto'
17 | decltype(std::declval<entity>().gen_lambda()) lambda_;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
and related.
There are two separate issues here, although at first glance one is templated to conflate them.
First, where you have:
decltype(std::declval<entity>().gen_lambda()) lambda_;
this entire declaration is not a complete-class context. (If there were a default member initializer for lambda_
, it would be the only part of the declaration of lambda_
that is a complete-class context.) Since it's not a complete-class context, you cannot perform a member access into entity
here, as it is an incomplete class. This can be demonstrated with a much simpler example:
struct S {
int foo() { return 0; }
decltype(std::declval<S>().foo()) x;
// ^^^^
// error: member access into incomplete class S
};
(Though, for reasons that are unclear to me, GCC does accept the above code.)
Exactly how you should work around this is unclear to me, because you've given no information about what problem you're trying to solve. One obvious technique (though I'm not sure whether it will fit your use case) is to change gen_lambda
into a static member function:
struct entity
{
template <class T>
static auto gen_lambda(T* p) {
return [p] { puts("Hello!"); };
}
entity()
: lambda_(gen_lambda(this))
{}
decltype(gen_lambda<entity>(nullptr)) lambda_;
};
This solves the issue with incompleteness, but you still might run into the "auto is used before it is deduced" error. As the linked answer mentions, the intent is to eventually fix the standard such that this code will be well-formed. In the meantime, two possible workarounds are:
Move gen_lambda
outside the class (e.g. make it a free function that is defined before S
); or
Turn the entire class into a template:
template <class U = void>
struct entity_impl
{
template <class T>
static auto gen_lambda(T* p) {
return [p] { puts("Hello!"); };
}
entity_impl()
: lambda_(gen_lambda(this))
{}
decltype(gen_lambda<entity_impl>(nullptr)) lambda_;
};
using entity = entity_impl<>;