Badmanchild
Badmanchild

Reputation: 1020

Template argument deduction fails on lambda using tuple

I'm trying to make a query function that lets me query an STL container for certain element characteristics, and then return the result set. It's really just syntactic sugar around normal STL operations (specifically, copy_if and a back_inserter).

#include <string>
#include <tuple>
#include <functional>
#include <vector>

// This is the query function, it compiles fine
template<typename T, typename ... U>
T query(T const& input, std::function<bool(std::tuple<U...> const& row)> pred)
{
    T result;
    std::copy_if(std::begin(input), std::end(input), std::back_inserter(result), pred);
    return result;
}

// Define my Row, with each column having a type
using Row = std::tuple<int, float, std::string>;

int main()
{
    // Create a sample vector of Rows
    std::vector<Row> vec;
    for(size_t i = 0; i < 100; ++i)
        vec.emplace_back(i, 5.0f, "hello");

    // This is how I would perform the query 
    // **** This is the line that doesn't compile due to template arg deduction failure ****
    auto result = query(vec, [](Row const& row) -> bool { return true; });
    return 0;
}

And here is the compiler output (Clang 3.3)

main.cpp:27:19: error: no matching function for call to 'query'
    auto result = query(vec, [](Row const& row) -> bool { return true; });
                  ^~~~~
main.cpp:8:3: note: candidate template ignored: failed template argument deduction
T query(T const& input, std::function<bool(std::tuple<U...> const& row)> pred)
  ^
1 error generated.

Upvotes: 0

Views: 422

Answers (1)

Praetorian
Praetorian

Reputation: 109189

A lambda is not an std::function, it is a class type with an overloaded operator(), the two are distinct types. The query function requires an std::function<...> argument, and template argument deduction requires types to be exact matches (besides cv-qualifiers), so the conversion from a lambda expression to std::function will never be deduced.

One solution, of course, is to construct an std::function before calling query

auto result = query(vec, 
                    std::function<bool(Row const&)>(
                        [](Row const&) { return true; }));

Another option is to change your function itself

template<typename T, typename UnaryPredicate>
T query(T const& input, UnaryPredicate&& pred)
{
    T result;
    std::copy_if(std::begin(input), std::end(input), 
                 std::back_inserter(result), std::forward<UnaryPredicate>(pred));
    return result;
}

Now it can be called using your original code.

Live demo

Upvotes: 2

Related Questions