Search code examples
c++templatesc++11c++14type-deduction

Automatic deduction of template parameters for alias templates and template classes


I want to make stateless lambda function with variadic template parameter list to be recursive. But I need type erasure to avoid errors like variable 'lambda' declared with 'auto' type cannot appear in its own initializer. Variadic template parameter list requires corresponding functional object to have templated operator (). For simple stateless lambda function I can cast it to pointer to simple free old function, but how to achieve a similar for variadic stateless lambda function? I think what I want is automatic type deduction for template parameter list (during instantiation of template variables: during call or during assignment): (pseudocode)

#include <type_traits>
#include <iostream>

#include <cstdlib>

template< typename first, typename second, typename ...rest >
using fp = first (*)(first const &, second const &, rest const &...); // at least binary function

int
main()
{
    template fp sum = [] (auto const & first, decltype(first) second, auto const &... rest) { return first + sum(second, rest...); };
    //              ^ assignment                                                                                ^ call
    std::cout << sum(1, 2.0, 3.0f) << std::endl;
    return EXIT_SUCCESS;
}

Is it possible to achieve such a behaviour currently (C++14) (say, using std::function or another way of type erasure)? Is there proposal for similar language feature? Or maybe it totally forbidden by already existent language rules?

Another possible example where it will be useful: (pseudocode)

template std::vector v{1, 2, 3};
static_assert(std::is_same< decltype(v), std::vector< int > >{});

Solution

  • No, there is no way to do what you want. The way to deduce the result of an expression is to use auto. There is no way to deduce types for a function template or alias template. Consider the simplest case:

    std::function<auto> if_this_existed = [](int x, int y) { return x + y; };
    

    You probably expect std::function<int(int, int)>. But std::function<void(int, int)> works. So does std::function<long(long, long)>. There really is no one thing to deduce. Furthermore, for a generic lambda, it doesn't make sense to assign a specific type:

    std::function<void(???)> print = [](auto x) { std::cout << x; };
    

    The lambda can be called with any type that's printable, but whatever we put in ??? would restrict print to be just that type. So this fails too.

    So ultimately, no, you cannot write your generic variadic lambda recursively. Although, sum would be impossible to write recursively anyway since you wouldn't be able to write a base case. The right way to write such a generic sum() would be to use a fold expression (C++1z):

    auto sum = [] (auto const&... args) {
        return (args + ...);
    };