1

I am currently working on on some code in which I need to use a lambda as a template parameter like in the following example:

#include <iostream>

template<void(*func)(int)>
struct FunctionCaller{
    FunctionCaller() {func(5);}
};

void RealFunction(int i){
    std::cout << i*2 << std::endl;
}

int main(){
    FunctionCaller<[](int i){std::cout << i << std::endl;}> caller; //should print 5
    FunctionCaller<RealFunction> caller2; //should print 10
    return 0;
}

I tried compiling this example with recent (C++17 supporting) versions of both GCC and Clang (versions 8.2.1 and 7.0.0 respectively) to get various errors that amount to lambda expressions not being allowed in template arguments. I am using the correct compiler flags to enable C++17 support.

This, however, seems to me like it should work because the book C++ Primer 5th edition says that any constant expression can be used for non-type template arguments, and in C++17, and this Stack Overflow post tells me that a lambda is a constant expression in C++17 and later.

After extensive research, it seems like the C++17 standard explicitly forbids lambdas as a template parameter. What is the rationale behind this restriction? What blows up if it is not in place?

EDIT: This question has some relevant information, but it only says that this restriction exists to prevent lambdas from appearing in a method signature, without any information as to why that is problematic.

john01dav
  • 1,842
  • 1
  • 21
  • 40
  • 1
    This question is not a duplicate of the linked question as that question relates to the usage of STL templates, and this question is about creating new class templates. Additionally, the linked question's accepted answer says that templates can't be used in a decltype-like context (unevaluated), but, surely, instantiating a template that calls a lambda evaluates that lambda. – john01dav Dec 03 '18 at 04:40
  • You can also look at https://stackoverflow.com/questions/1174169/function-passed-as-template-argument which can solve your problem – Vasanth Alagiriswamy Dec 03 '18 at 04:42
  • @VasanthAlagiriswamy I am aware of a few workarounds, but the fact that I have no idea why my original solution isn't working shows a hole in my knowledge of C++ that I want to correct. – john01dav Dec 03 '18 at 04:44
  • Possible duplicate of [Will I be able to declare a constexpr lambda inside a template parameter?](https://stackoverflow.com/questions/44485610/will-i-be-able-to-declare-a-constexpr-lambda-inside-a-template-parameter) – Passer By Dec 03 '18 at 05:00
  • @PasserBy The only relevant information there is that this is indeed intentional, and that for some reason lambdas in method signatures is a problem. It isn't clear *why* that is a problem. – john01dav Dec 03 '18 at 05:03
  • @john01dav I don't know why exactly this was not allowed, but probably due to ODR concerns (see point 6 in [P0315](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0315r4.pdf) which changed this behavior in C++20). – Rakete1111 Dec 03 '18 at 05:31

1 Answers1

4

After some searching I found the change that introduced this restriction: core issue 1607. It says:

Further discussion has arisen regarding lambda-expressions in function template signatures. Although the restriction that lambda-expressions cannot appear as unevaluated operands (8.1.5 [expr.prim.lambda] paragraph 2) was intended to avert the need to deal with them in function template signatures, the fact that 8.20 [expr.const] treats unevaluated subexpressions separately from unevaluated operands opens another avenue for lambda-expressions in template signatures, e.g.,

template<typename T>
void f(int [(0 && [] { for (auto x : T()) {} }, 1)]);

Core gave EWG 4 options, and they choose the one we have in C++17 and before: Disallow lambdas in the signature - so template arguments, etc.

Now why should lambdas not appear in a function template signature? This is explained in N2903:

In addition, this rewrite adds the restriction that lambda expressions cannot be used in the operand of a sizeof operator, alignof operator, or decltype specifier. That restriction — suggested by Doug Gregor and John Spicer — avoids severe implementation difficulties with template argument deduction (e.g., this avoids the need to encode arbitrary statement sequences in mangled names).

Community
  • 1
  • 1
Rakete1111
  • 47,013
  • 16
  • 123
  • 162