Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

g++ compiler error: couldn't deduce template parameter ‘_Funct’

I'm trying to use an ANSI C++ for_each statement to iterate over and print the elements of a standard vector. It works if I have the for_each call a non-overloaded function, but yields a compiler error if I have it call an overloaded function.

Here's a minimal test program to show where the compiler error occurs:

#include <algorithm>
#include <iostream>
#include <vector>

struct S {
    char c;
    int i;
};
std::vector<S> v;

void print_struct(int idx);
void print_struct(const struct S& s);

// f: a non-overloaded version of the preceding function.
void f(const struct S& s);

int main()
{
    v.push_back((struct S){'a', 1});
    v.push_back((struct S){'b', 2});
    v.push_back((struct S){'c', 3});

    for (unsigned int i = 0; i < v.size(); ++i)
        print_struct(i);

    /* ERROR! */
    std::for_each(v.begin(), v.end(), print_struct);

    /* WORKAROUND: */
    std::for_each(v.begin(), v.end(), f);

    return 0;
}

// print_struct: Print a struct by its index in vector v.
void print_struct(int idx)
{
    std::cout << v[idx].c << ',' << v[idx].i << '\n';
}

// print_struct: Print a struct by reference.
void print_struct(const struct S& s)
{
    std::cout << s.c << ',' << s.i << '\n';
}

// f: a non-overloaded version of the preceding function.
void f(const struct S& s)
{
    std::cout << s.c << ',' << s.i << '\n';
}

I compiled this in openSUSE 12.2 using:

g++-4.7 -ansi -Wall for_each.cpp -o for_each

The full error message is:

for_each.cpp: In function ‘int main()’:
for_each.cpp:31:48: error: no matching function for call to ‘for_each(std::vector<S>::iterator, std::vector<S>::iterator, <unresolved overloaded function type>)’
for_each.cpp:31:48: note: candidate is:
In file included from /usr/include/c++/4.7/algorithm:63:0,
                 from for_each.cpp:5:
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note: template<class _IIter, class _Funct> _Funct std::for_each(_IIter, _IIter, _Funct)
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note:   template argument deduction/substitution failed:
for_each.cpp:31:48: note:   couldn't deduce template parameter ‘_Funct’

I don't see any search results for this particular error on Stack Overflow, or on the web generally. Any help would be appreciated.

like image 445
Spacewaster Avatar asked Sep 28 '12 10:09

Spacewaster


2 Answers

A names refers to an overload set. You'll need to specify which overload you want:

std::for_each(v.begin(), v.end(), (void (&)(S const&)) print_struct);

Another approach is to use a polymorphic callable function object as a helper:

struct PrintStruct
{
    template <typename T> void operator()(T const& v) const 
        { return print_struct(v); }
};

int main()
{
    PrintStruct helper;

    std::vector<S> sv;
    std::vector<int> iv;

    // helper works for both:
    std::for_each(sv.begin(), sv.end(), helper);
    std::for_each(iv.begin(), iv.end(), helper);
like image 196
sehe Avatar answered Sep 17 '22 08:09

sehe


std::for_each declaration looks like this:

template<class InputIter, class Func>
void for_each(InputIter first, InputIter last, Func func);

As you can see, it takes anything you give it as the third parameter. There is no restriction that it has to be a callable type of a certain signature or a callable type at all.

When dealing with overloaded functions, they're inherently ambiguous unless you give them some context to select the right one. In a call to an overloaded function, this context are the arguments you pass. When you need a pointer, however, you can't use arguments as a context, and the for_each parameter also doesn't count as a context, since it takes anything.

As an example of where a function parameter can be a valid context to select the right overload, see this:

// our overloads
void f(int){}
void f(double){}

typedef void (*funcptr_type)(int);
void g(funcptr_type){}

// ...
g(&f); // will select 'void f(int)' overload, since that's 
       // the only valid one given 'g's parameter

As you can see, you give a clear context here that helps the compiler select the right overload and not have it ambiguous. std::for_each's parameters do not give such a context, since they take anything.

There are two solutions:

  • manually provide the context either by
    • casting to the right function pointer type, or
    • using an intermediate variable of the right type and passing that
  • use a non-overloaded function that dispatches to an overloaded one (as you did with f)

Note that in C++11, you could also use a lambda for the second option:

std::for_each(v.begin(), v.end(), [](const S& s){ print_struct(s); });

Some notes on your code:

  • (struct S){'a', 1} is a compound literal and not standard C++
  • you don't need struct S in C++, only S suffices
like image 23
Xeo Avatar answered Sep 21 '22 08:09

Xeo