I'm trying to create a templated function which accepts an iterable and a function, such that the passed function will be implicitly casted to a std::function
of the appropriate type (thus allowing it to be used with both full functions and lambdas).
Here's the code:
#include <iostream>
#include <vector>
#include <algorithm>
#include <typeinfo>
template<typename T>
void bar(const T & base, std::function<bool(int)> f) // works
//void bar(const T & base, std::function<bool(typename T::iterator::value_type)> f) // fails to compile
{
std::cout << ((typeid(std::function<bool(int)>) == typeid(std::function<bool(typename T::iterator::value_type)>))?"identical":"distinct") << std::endl;
}
bool filter(int x) { return x%2==0; }
int main() { bar(std::vector<int> {0, 1}, filter); }
Compiled with g++-4.7 -std=c++11 -o itest itest.cpp
this produces identical
.
If you uncomment line 10 and comment line 9 and compile as above, instead compilation fails with
g++-4.7 -std=c++11 -Wall -Werror -o itest itest.cpp
itest.cpp: In function 'int main()':
itest.cpp:16:53: error: no matching function for call to 'bar(std::vector<int>, bool (&)(int))'
itest.cpp:16:53: note: candidate is:
itest.cpp:9:10: note: template<class T> void bar(const T&, std::function<bool(typename T::iterator::value_type)>)
itest.cpp:9:10: note: template argument deduction/substitution failed:
itest.cpp:16:53: note: mismatched types 'std::function<bool(typename T::iterator::value_type)>' and 'bool (*)(int)'
I should note that the unmodified version succeeds with Xcode (having set the appropriate options), but I'd prefer to stick with g++ over clang if possible. Am I doing something wrong, or is this a known bug in g++?
Sorry, but the bug is in your code. It's equivalent to:
template<typename T> struct S { template<typename U> S(const U &); };
template<typename T> void bar(T, S<T>);
int main() { bar(5, 6); }
The issue is that in template argument deduction/substitution, if a template argument appears (either directly or in constructing a dependent type) in more than one argument then both arguments have to match exactly; user-defined conversions are not considered, even if it is obvious from one argument what the type has to be.
The user-defined conversion here is the implicit constructor of std::function<...>
.
Possible fix would be to instantiate bar
explicitly (as bar<int>
), or to dispatch to a helper function:
template<typename T>
void bar_impl(const T & base, std::function<bool(typename T::iterator::value_type)> f)
{
std::cout << ((typeid(std::function<bool(int)>) == typeid(std::function<bool(typename T::iterator::value_type)>))?"identical":"distinct") << std::endl;
}
template<typename T, typename F>
void bar(const T & base, F &&f)
{
bar_impl<T>(base, std::forward<F>(f));
}
You need second overload for pointer function -- then it will compile. Implicit cast to std::function
wont work:
void bar(const T & base, bool(*f)(typename T::value_type)){
std::cout << "ptr func\n";
}
Work around for problem described by ecatmur (several T, with not matching types in function signutre): you can wrap other T in identity
struct, which is defined like so:
template<class T> struct identity{ typedef T type; };
then compiler will ignore these T's for type deduction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With