Question

What are the differences between

template <typename T> void func( T t ) { /* ... */ }

and the C++14 alternative using lambdas with auto parameters?

auto func = []( auto t ) { /* ... */ }

Which one should be preferred?

Was it helpful?

Solution

The first is a function template. It can be specialized and overloaded. It can be found by ADL. When you want to take the address, you must either explicitly give it template parameters or do it in a context where the compiler can deduce them.

The second, assuming it appears on namespace scope, is a global object with a templated function call operator. It cannot be specialized or overloaded (global variables conflict with functions, they don't overload them). It cannot be found by ADL (ADL only finds functions and function templates). If you use the address operator on it, you get the address of the object, which is pretty useless. The object itself can be converted to a function pointer if the compiler can deduce the arguments; you cannot supply them explicitly.

You can use whichever you want; just be aware of the advantages and disadvantages of either choice. I would recommend the first. The only advantage of the second is its terseness, and I hope we'll get terse syntax for function templates in the not-too-distant future as well.

auto func(auto t) { ... }

OTHER TIPS

The difference is that the first one is function template which you have to define before you use it; once the definition is there, anyone can use it. So it is a reusable piece of code and remains there forever.

Lambdas, on the other hand, are handy: you can define it when you need it. If the lambda is defined inside a function, as a local object, then only that function can use it and pass it to other functions. It is still reusuable, but less than function template. However lambdas, defined at namespace level, is as reusable as function template, because anyone can use it. So it is not much different from function template when you define it at namespace level. There can be some corner cases to be discovered by experts. One such case is, you can specialize function template:

//specialization : possible only in case of template!
template<> void func(MyClass obj) { /* ... */ }

You cannot do this with lambdas!

N3337, [expr.prim.lambda]/3:

The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type — called the closure type — whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.

This closure type will stay a class. But its overloaded function call operator will be a operator function template, allowing different specializations. Furthermore, unlike function templates, you can implicitely convert a closure object to a function pointer. Its really handy, isn't it? Quoting N3559, it'll look something like that:

For a generic lambda L:

int(*fp)(int, char) = [](auto a, auto b){return a+b;};

The closure type is

struct/*anonymous*/
{
    template<class A,class B>
    auto operator()(A a,B b) const
    {
        return a+b;
    }

    private:
        template<class A,class B>
        static auto __invoke(A a,B b)
        {
            return a+b;
        }

        template<class A,class B,class R>
        using fptr_t = R(*)(A,B);

    public:

        template<class A,class B,class R>
        operator fptr_t<R,A,B>() const
        {
            return &__invoke<A,B>; // Fixed that manually, they forgot to qualify the template name
        }
} L;

int(*fp)(int,char) = L;

(There will be usual template argument deduction performed)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top