This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 108i. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2022-07-16


1607. Lambdas in template parameters

Section: 7.5.5  [expr.prim.lambda]     Status: C++14     Submitter: Daniel Krügler     Date: 2013-01-19

N3690 comment CA 3

[Moved to DR at the February, 2014 meeting.]

Lambda expressions cannot appear in unevaluated operands nor in evaluated portions of constant expressions. However, the following example appears to circumvent those restrictions:

  template <bool> struct BoolSink { typedef void type; };

  template <typename T, typename U>
  struct AddRvalueReferenceImpl { typedef T type; };

  template <typename T>
  struct AddRvalueReferenceImpl<T, typename BoolSink<false &&
     [] {
       extern T &&tref;
     }>::type> {
    typedef T &&type;
  };

  template <typename T>
  struct AddRvalueReference : AddRvalueReferenceImpl<T, void> { };

  namespace ImplHelpers {
    template <typename T>
    typename AddRvalueReference<T>::type create(void) { }
  }

  template <typename T, typename U, typename ...Args>
  struct IsConstructibleImpl { enum { value = 0 }; };

  template <typename T, typename ...Args>
  struct IsConstructibleImpl<T, typename BoolSink<false &&
     [] {
       T t( ::ImplHelpers::create<Args>() ...);
     }>::type, Args ...> {
    enum { value = 1 };
  };

  template <typename T, typename ...Args>
  struct IsConstructible : IsConstructibleImpl<T, void, Args ...> { };

  struct DestroyMe {
    ~DestroyMe() = delete;
  };

  static_assert(+IsConstructible<int>::value, "error");
  static_assert(!IsConstructible<void>::value, "error");
  static_assert(+IsConstructible<int [1]>::value, "error");
  static_assert(!IsConstructible<DestroyMe>::value, "error");
  static_assert(!IsConstructible<int *, char *>::value, "error");

  static_assert(+IsConstructible<int &&, int>::value, "error");
  static_assert(!IsConstructible<int &&, int &>::value, "error");
  static_assert(+IsConstructible<int &&, int &&>::value, "error");

Is this intended?

Additional notes, April, 2013:

Further discussion has arisen regarding lambda-expressions in function template signatures. Although the restriction that lambda-expressions cannot appear as unevaluated operands (7.5.5 [expr.prim.lambda] paragraph 2) was intended to avert the need to deal with them in function template signatures, the fact that 7.7 [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)]);

Four possible approaches for dealing with this issue have been suggested:

  1. Allow lambda-expressions in function template signatures. This would be costly in some implementations.

  2. Give a function template internal linkage if its signature includes a lambda-expression. This would allow SFINAE and redeclaration to work without requiring that lambda-expressions be mangled.

  3. Specify that a function signature containing a lambda-expression is not a redeclaration of any other function template, which would allow SFINAE to work but not require declaration matching and mangling.

  4. Do not allow lambda-expressions in function template signatures.

If any of these approaches were adopted, the rationale for disallowing lambda-expressions in unevaluated operands would be removed, so it might make sense to remove the restriction at the same time.

Proposed resolution (September, 2013):

Change 7.5.5 [expr.prim.lambda] paragraph 2 as follows:

...A lambda-expression shall not appear in an unevaluated operand (Clause 7 [expr]), in a template-argument, in an alias-declaration, in a typedef declaration, or in the declaration of a function or function template outside its function body and default arguments. [Note: The intention is to prevent lambdas from appearing in a signature —end note]. [Note: A closure object behaves like a function object (22.10 [function.objects]). —end note]