Calculated constexpr lambda in a template argument without type

Lambda expressions are not allowed in unappraised contexts (for example, decltype) and cannot be constant expressions until recently. Therefore, it was not possible to use them in the template arguments.

In C ++ 17, a constant lambdas expression is possible. This still prevents them from being used in template arguments at all.

However, specifically for template arguments without a type, a constant expression lambda expressions can be used in the evaluated context, for example:

template<int N> struct S { constexpr static int value = N; }; int main() { int N = S<[]()constexpr{return 42;}()>::value; } 

This still does not work, because lambda expressions are explicitly forbidden in the template arguments, whether type or non-type.

My question is a reasoning that does not allow to build higher. I can understand that lambdas types in function signatures can be problematic, but here the closure type does not matter, only the return value is used (compile-time constant).

I suspect that the reason is that all statements in the lambda body will become part of the expression of the template argument, and therefore SFINAE will need to be applied if any statement in the body is poorly formed during the replacement. This will probably require considerable work from compiler developers.

But this is actually my motivation. If one could use the construction above, then SFINAE could be used not only with constant expressions, but also with other operators valid in constexpr-functions (for example, declarations of the literal type).

Besides the impact on the authors of the compiler, are there any problems that might cause, for example. ambiguities, contradictions, or complications in the standard?

+5
source share
1 answer

It is deliberate that lambdas do not appear in unappreciated contexts. The fact that lambda always has unique types leads to different questions.

Here are some examples from the comp.lang.C ++ discussion from Daniel Krugler:

There really would be a huge number of precedents that allow lambda expressions, this will probably greatly expand the possible cases of sfain (include the full code for sandboxes). The reason that they became an exception was due to this extreme expansion of sfinae cases (you opened the Pandora box for the compiler) and the fact that it can lead to problems with other examples like yours, for example.

 template<typename T, typename U> void g(T, U, decltype([](T x, T y) { return x + y; }) func); 

useless as each lambda expression generates a unique type, so something like

 g(1, 2, [](int x, int y) { return x + y; }); 

actually doesn't work because the lambda type used in the parameter is different from the lambda type in the g call.

Finally, it also caused name problems. For instance. when you have

 template<typename T> void f(T, A<sizeof([](T x, T y) { return x + y; })> * = 0); 

in one translation unit, but

 template<typename T> void f(T, A<sizeof([](T x, T y) { return x - y; })> * = 0); 

in another translation unit. Suppose you create an instance of f<int> from both translation units. These two functions have different signatures, so they must create templates with a modified instantiation template. The only way to separate them is with the lambda body. This, in turn, means that the authors of the compiler have come up with rules for managing names for each type of statement in the language. Although technically feasible, it was considered a specification and workload.

This is a whole series of problems. Especially considering that you motivate to write:

 int N = S<[]()constexpr{return 42;}()>::value; 

can be easily solved by writing:

 constexpr auto f = []() constexpr { return 42; } int N = S<f()>::value; 
+2
source

Source: https://habr.com/ru/post/1257531/


All Articles