Tobias Hermann
Tobias Hermann

Reputation: 10926

template argument deduction of return type in function template

Given the following minimal example:

#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <set>
#include <vector>

template<typename ContainerOut, typename X, typename Y, typename ContainerIn>
ContainerOut Map( const ContainerIn& xs, const std::function<Y( const X& )>& f )
{
    ContainerOut ys;
    std::transform( begin( xs ), end( xs ), std::inserter( ys, end( ys ) ), f );
    return ys;
}

struct Foo {
    Foo( int val ) : val_( val ) {}
    int val_;
};

std::set<int> FooValsToIntSet( const std::list<Foo>& foos )
{
    //Map<std::set<int>, Foo, int>
    return Map( foos, []( const Foo& foo )
    {
        return foo.val_;
    } );
}

int main()
{
    std::list<Foo> foos = { 1, 2, 2, 3 };
    std::set<int> vals = FooValsToIntSet( foos );
    for ( auto& v : vals )
        std::cout << v << std::endl;
}

The line

return Map( foos, []( const Foo& foo )

is not accepted by the compilers I tested.

It works if I write out the template arguments explicitly instead:

return Map<std::set<int>, Foo, int>( foos, []( const Foo& foo )

But I do not understand why this is necessary. Is there a way to avoid this verbosity?

Upvotes: 1

Views: 385

Answers (2)

Rostislav
Rostislav

Reputation: 3977

Well, the compiler has no way of deducing the ContainerOut type - the return type doesn't participate in type deduction. However, you could let the compiler deduce everything except the return type. Just as a side note, at least in this case, there is no reason to use std::function - it's just adding unnecessary overhead.

This would be better:

template<typename ContainerOut, typename ContainerIn, typename F>
ContainerOut Map( const ContainerIn& xs, F&& f )
{
    ContainerOut ys;
    std::transform( begin( xs ), end( xs ), std::inserter( ys, end( ys ) ), f );
    return ys;
}

and then you can call it as

return Map<std::set<int>>( foos, []( const Foo& foo )
{
    return foo.val_;
} );

Upvotes: 7

TheOperator
TheOperator

Reputation: 6436

You don't seem to need the actual template parameters inside Map(), so you can deduct the function object as a single parameter:

template<typename ContainerOut, typename ContainerIn, typename Function>
ContainerOut Map( const ContainerIn& xs, const Function& f )
{
    ...
}

This is also how STL algorithms taking a predicate do it.

Upvotes: 0

Related Questions