Can lambdas be used as non-type template parameter?

前端 未结 3 821
野性不改
野性不改 2021-01-03 00:02

Is the following code legal?

template 
struct A {};

int main () {
  auto lmb = [](int i){return i*i;};
  A a;
  return 0;
}
         


        
3条回答
  •  佛祖请我去吃肉
    2021-01-03 00:50

    [temp.arg.nontype]/1:

    If the type T of a template-parameter contains a placeholder type ([dcl.spec.auto]) or a placeholder for a deduced class type ([dcl.type.class.deduct]), the type of the parameter is the type deduced for the variable x in the invented declaration

    T x = template-argument ;
    

    If a deduced parameter type is not permitted for a template-parameter declaration ([temp.param]), the program is ill-formed.

    So, the rules are set by [temp.param]/6:

    A non-type template-parameter shall have one of the following (possibly cv-qualified) types: ...

    (6.1) a structural type ...

    The rules for structural type are: --my emphasis--

    (7.1) a scalar type, or

    (7.2) an lvalue reference type, or

    (7.3) a literal class type with the following properties:

    (7.3.1) all base classes and non-static data members are public and non-mutable and

    (7.3.2) the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.

    Since the lambda has no base class, the only requirement is that it has to be a literal class type ([basic.types]) which includes:

    (10.5.2) ... a closure type ([expr.prim.lambda.closure]) ...

    The data members of a structural type shall also be structural type, this applies to the lambda's capture in this case, as long as all its members are public and non-mutable.


    @Nicol Bolas commented below that a lambda with captures, even if constexpr literal type captures, is not mandated by the standard to manage the captures as public fields.


    The bottom line is that in C++20 a constexpr lambda expression without a capture shall be legal as a template non-type argument (based on [basic.types]/10.5.2 mentioned above).

    See also an answer by @Barry to a similar question.


    Below code compiles with gcc, but as I understand from the comment by Nicol Bolas, not all cases are guaranteed by the spec (or even worse, all cases are not guaranteed by the spec?).


    Suppose we have:

    template  struct A {};
    
    struct B {};
    
    struct C {
        ~C(){}
    };
    

    Literal type lambdas, that shall be legal as template arguments:

    // compiles in gcc and should be ok by the spec as of [basic.types]/10.5.2
    A<[](){}> a; // compiler deduces the anonymous lambda to be constexpr
    
    auto lmb1 = [](){};
    // same as above
    A a1;
    
    // compiler deduces lmb1 above to be constexpr
    // same as it will deduce the following:
    B b {};
    A here_i_am;
    

    Lambdas, that are compiled by gcc as template arguments, but as Nicol Bolas argues in the comment - the spec doesn't guarantee them to be literal types:

    const int i = 0;
    constexpr auto lmb2 = [i](){};
    // compiles in gcc but is not guaranteed by the spec 
    A a2;
    
    constexpr auto lmb3 = [b](){}; // B is literal
    // compiles in gcc but is not guaranteed by the spec 
    A a3;
    

    Non-literal type lambdas, not legal as template arguments:

    const int j = 0;
    // below doesn't compile: {j} is not a constant expression
    constexpr auto lmb4 = [&j](){}; // local reference - not constexpr
    A a4;
    
    C c;
    // below doesn't compile: '{c} does not have 'constexpr' destructor
    constexpr auto lmb5 = [c](){}; // C is not literal
    A a5;
    

提交回复
热议问题