Search code examples
c++lambdac++17template-specializationc++-experimental

Can a lambda instantiate a template function?


Questions regarding the use of C++14 generic lambdas or C++20 template lambdas are typically about generating lambdas with the appropriate parameterized types.

My question is, is it possible for a lambda parameter, or its evaluation, to force the instantiation (or specialization) of a template, e.g., a template function? The parameter (n) would need to be qualified as constexpr for this to work.

template <int n> ret_type fn (...) {...}
...
auto fx = [] (int n) { return fn<n>(...) }

I'm not completely up to date with C++20, or newer working proposals, and admit there are still nuances with constexpr, etc., in C++17 lambdas and other edge features, that have me looking up cppreference, Josuttis, and others pretty frequently.

I know this close to an XY-problem. Since template instantiation is performed at compile time, a lambda expression for a template parameter seems like an anti-pattern. But since templates can be instantiated if types and constant values are known at compile time, are there any proposals to allow such a mechanism?


Solution

  • The answer to your question is technically yes, lambda bodies can instantiate template functions. The actual example doesn't work, because int n as a parameter can't be used that way.

    There is an easy workaround

    template<auto x>
    using constant_t = std::integral_constant< std::decay_t<decltype(x)>, x >;
    template<auto x>
    constexpr constant_t<x> constant = {};
    
    template <int n> int fn () { int arr[n] = {0}; return sizeof(arr); }
    auto fx = [] (auto n) { return fn<n>(); };
    std::cout << fx( constant<3> );
    

    Live example.

    Here I made the constant<x> variable template that creates an instance of std::integral_constant<X, x>. This is a stateless (but not valueless!) type that has a constexpr conversion to its value.

    We can pass that to a lambda, and so long as the lambda takes it by value we can then convert it to a constexpr value within the lambda, including passing it as a template non-type parameter, instantiating a template function specialization as you are asking for.

    The can be done without the constant variable template, ie if you don't have auto parameter support:

    template<std::size_t N>
    using index_t = std::integral_constant<std::size_t, N>;
    template<std::size_t N>
    constexpr index_t<N> index = {};
    

    we can use a type-specific version of it, and just pass that, and it works the same way.


    Aside, constant<?> is fun. For example:

    using upFILE=std::unique_ptr<
      std::FILE,
      constant_t<std::fclose>
    >;
    
    upFILE file( fopen("hello.txt", "r") );
    

    does the right thingtm.