Marson Mao
Marson Mao

Reputation: 3035

Why can't I deduce template argument for this function?

I try to make a function template, remove a Value from a vector, giving a Key:

template
<
    typename Value,
    typename Map,
    typename Key
>
void RemoveValue(
    Map& m, 
    const Key& k, 
    const Value& v,
    std::function<bool(const Value&)> f)
{
    auto it1 = m.find(k);
    if (it1 == m.end())
    {
        return;
    }
    auto vs = it1->second;
    auto it2 = std::find_if(vs.begin(), vs.end(), f);
    if (it2 == vs.end())
    {
        return;
    }
    vs.erase(it2);
    if (vs.empty())
    {
        m.erase(it1);
    }
}

When I use it:

RemoveValue(entityToTags, &entity, tag, [&](const Tag& t){ return t == tag; });

With:

const Entity& entity, const Tag& tag

std::map<const Entity*, std::vector<Tag>> entityToTags;

I must specify Tag, i.e. RemoveValue<Tag>(entityToTags, &entity, tag, [&](const Tag& t){ return t == tag; }); to successfully compile.

How can I not explicitly specify <Tag>, and let the compiler know it?

I am using VS2012.

Thanks!

Upvotes: 2

Views: 162

Answers (2)

rodrigo
rodrigo

Reputation: 98368

You should add the error message to your question...

Anyway, I used CLang++ to compile it and the error is:

test.cpp:47:5: error: no matching function for call to 'RemoveValue'
    RemoveValue(entityToTags, &entity, tag, [&](const Tag &t) { return t == tag; });
    ^~~~~~~~~~~
test.cpp:15:6: note: candidate template ignored: could not match 'function<bool (const type-parameter-0-0 &)>' against '<lambda at t.cpp:47:45>'
void RemoveValue(

That is, to deduce the type of Tag the compiler must use the 3rd argument tag and the 4th. The last one is of type std::function<bool(const Value&)> but you pass a lambda. Yes, the lambda is convertible to that type, but it is not that type, so the automatic deduction for Tag fails.

The solution would be to pass a value of real type std::function<bool(const Value&)>:

std::function<bool(const Tag&)> f = [&](const Tag &t) { return &t == &tag; };
RemoveValue(entityToTags, &entity, tag, f);

Or use static_cast<>(), if you prefer, but I would find that line too long.

Upvotes: 2

You could simply take an f of arbitrary type, instead of forcing it into std::function. It makes more sense, as it could even be fore efficient, and the standard library does that extensively. So change the function like this:

template
<
    typename Value,
    typename Map,
    typename Key,
    typename Functor
>
void RemoveValue(
    Map& m, 
    const Key& k, 
    const Value& v,
    Functor f)
{
    auto it1 = m.find(k);
    if (it1 == m.end())
    {
        return;
    }
    auto vs = it1->second;
    auto it2 = std::find_if(vs.begin(), vs.end(), f);
    if (it2 == vs.end())
    {
        return;
    }
    vs.erase(it2);
    if (vs.empty())
    {
        m.erase(it1);
    }
}

Upvotes: 4

Related Questions