Search code examples
c++lambdac++20constexprnon-type-template-parameter

Why can't a stateful but `constexpr` lambda not be used as a non-type template parameter?


Somehow I still think of lambdas as "syntactic sugar" for regular function objects, so it surprises me that under C++-20 a stateful but otherwise constexpr lambda instance cannot be used as a non-type template parameter, unlike an equivalent function object instance.

Can anyone explain this behavior or decision?

Example on godbolt:

struct always_fn {
    const int x;

    int operator()() const
    {
        return x;
    }
};
inline constexpr always_fn always_f{5};

// lambda equivalent to `always_f`
inline constexpr auto always_2_f = [x = 5]() {
    return x;
};

template<typename F>
struct wrapped {
    F f_;
};
inline constexpr auto wrapped_f = wrapped{always_f};
inline constexpr auto wrapped_2_f = wrapped{always_2_f};

template<auto f>
void pass() {}

int main() {
    pass<always_f>();
    pass<wrapped_f>();
    // error: no matching function for call to 'pass'
    pass<always_2_f>();
 // ^~~~~~~~~~~~~~~~
    // error: no matching function for call to 'pass'
    pass<wrapped_2_f>();
 // ^~~~~~~~~~~~~~~~~

    return 0;
}

Solution

  • Lambdas (whether stateful or not) are never structural types and therefore can never be used as non-type template argument.

    This was clarified by CWG 2542 which compilers might not implement yet.

    Without making such a decision, whether or not the lambda has a structural type would depend on implementation details such as whether the members of the closure type are private/public or the implementation of the closure type would have to be specified in much more detail.

    In particular, there is nothing requiring that x in your equivalent function object should be public and if it isn't, then it wouldn't be usable as non-type template argument either.