20
votes

I'm having lambdas that don't capture anything, like

[](){};

I have a template class, that contains such a lambda. Since the lambda does not contain non-static data members, nor virtual functions, it should be an empty class and DefaultConstructible. It's only a kind of policy class usable for template metaprogramming. I would like to know, why such a class is not default constructible by the C++ standard.

Sidenote: Understanding how Lambda closure type has deleted default constructor is asking a different question, though the title seems to be very similar. It is asking how a stateless lambda-object is created without usable default constructor. I'm asking why there is no usable default constructor.

3
This question might be relevant. - Ami Tavory
How would you default-construct a lambda? []{} already constructs one and gives you the instance. - rustyx
@rustyx Like so: auto f = ()[]{}; using f_t = decltype(f); f_t{}; — this works, but for the fact that lambdas have deleted default constructors. - Konrad Rudolph
@RichardCritten Interesting in itself, but does that change anything wrt this question? The change from 'deleted' to 'no' default constructor was already noted in the answer linked by Ami Tavory. - underscore_d
"It's only a kind of policy class usable for template metaprogramming." Then make it an actual class. struct Typename{}; is not that much longer than your lambda expression. And it has the added benefits of not creating an object of that type when you're only going to use the type for policies. Lambdas do not exist as a quick means for building structs. - Nicol Bolas

3 Answers

10
votes

Lambdas are intended to be created then used. The standard thus says "no, they don't have a default constructor". The only way to make one is via a lambda expression, or copies of same.

They are not intended for their types to be something you keep around and use. Doing so risks ODR violations, and requiring compilers to avoid ODR violations would make symbol mangling overly complex.

However, in C++17 you can write a stateless wrapper around a function pointer:

template<auto fptr>
struct function_pointer_t {
  template<class...Args>
  // or decltype(auto):
  std::result_of_t< std::decay_t<decltype(fptr)>(Args...) >
  operator()(Args&&...args)const
    return fptr(std::forward<Args>(args)...);
  }
};

And as operator void(*)() on [](){} is constexpr in C++17, function_pointer_t<+[](){}> is a do-nothing function object that is DefaultConstructible.

This doesn't actually wrap the lambda, but rather the pointer-to-function that the lambda produces.

2
votes

I'll assume that you're familiar with the difference between types, objects and expressions. In C++, lambda specifically refers to a lambda expression. This is a convenient way to denote a non-trivial object. However, it's convenience: you could create a similar object yourself by writing out the code.

Now per the C++ rules every expression has a type, but that type not what lambda expressions are intended for. This is why it's an unnamed and unique type - the C++ committee didn't think it worthwhile to define those properties. Similarly, if it was defined to have a default ctor, the Standard should define the behavior. With the current rule, there's no need to define the behavior of the default ctor.

As you note, for the special case of [](){} it's trivial to define a default ctor. But there's no point in that. You immediately get to the first hard question: for what lambda's should the default ctor be defined? What subset of lambda's is simple enough to have a decent definition, yet complex enough to be interesting? Without a consensus, you can't expect this to be standardized.

Note that compiler vendors, as an extension, could already offer this. Standardization often follows existing practice, see Boost. But if no compiler vendor individually thinks it worthwhile, why would they think so in unison?

0
votes

With C++20, stateless lambdas are default constructible. So, now this kind of operations are valid:

auto func = []{};
decltype(func) another_func;