Reputation: 651
Here is the problem. The code at the bottom of the forum topic is just the most simple example of what I think is a compiler error reproduced in case of overloaded function deduction based just on argument types. There is a function:
void f(const T& v, const std::function< bool( const T& ) >& validator );
... that takes some value and a validator. So, since there is no need to validate a bool there is a validator-less overloaded version of the function:
void f(const bool& v);
To be most precise, the generation of a function, taking bool as a value, by the function template should be disabled:
template<bool>
void f(const bool& v, const std::function< bool(const bool&) >& validator) = delete;
... but that is not made for the sake of code simplicity.
If one compiles the code using MSVC12, included in MSVS2013, the code at the bottom of the topic would not compile. Both lines:
f(5, [](const int& a){ return a != 0; });
f(5, & isIntValid );
... will issue a C2660 error telling that "f" does not take 2 arguments or there is no such overloaded version that takes that as much arguments. This, of course, is a total lie and a compiler bug. Now, comment the line:
#define USE_FUNCTOR
... so that to enable the version of "f" with function pointer argument as a validator instead of a functor. Then the code compiles perfectly well. If you like replace the function pointer argument with a plain bool one:
template< typename T >
void f(const T& v, const bool isValid )
{
if (isValid)
{
std::cout << v;
}
}
... and then call the function:
f( 5, false );
There won't be any compiler errors again. An explicit fix: call the template version of f by explicitly specifying the template argument type: The lines:
f(5, [](const int& a){ return a != 0; });
f(5, & isIntValid );
... should become:
f<int>(5, [](const int& a){ return a != 0; });
f<int>(5, & isIntValid );
But, as obvious such differentiated usage is not uniform accross all "f" overloads. Note that the bug is not reproduced when using non-template code:
void b(const int a, const std::function< bool(const int& v) >& validator)
{
if (validator(a))
{
std::cout << a;
}
}
void b(const int a)
{
std::cout << a;
}
// ...
b(5, [](const int& a){ return a != 0; });
b(5);
The conclusion: using std::function as a parameter in a template function that is overloaded by a non-template one gives unexpected wrong overloaded function deduction results.
The question: how to pass a std::function as a parameter to a template function without sacrificing the convenience of not having to explicitly state the function template argument?
Here is the code reproducing the compiler bug:
#include <iostream>
#include <functional>
#define USE_FUNCTOR
#ifdef USE_FUNCTOR
template< typename T >
void f(const T& v, const std::function< bool( const T& ) >& validator )
{
if (validator(v))
{
std::cout << v;
}
}
#else
template< typename T >
void f(const T& v, bool(*validator)(const T& a))
{
if ((*validator)(v))
{
std::cout << v;
}
}
#endif
void f(const bool& v)
{
std::cout << (v ? "yes" : "no" );
}
bool isIntValid(const int& a)
{
return a != 0;
}
int main(int argc, char* argv[])
{
#ifdef USE_FUNCTOR
f(5, [](const int& a){ return a != 0; });
#endif
f(5, & isIntValid );
f(true);
return EXIT_SUCCESS;
}
Upvotes: 2
Views: 489
Reputation: 171127
The problem is that T
is in a deduced context in both the parameter v
and the parameter validator
, which means the caller would have to provide an actual std::function
object for the deduction to work. A lambda is not of type std::function
.
Since you want deduction on the first parameter only, a helper to make the second T
occurrence non-deduced is what you need:
template <class T>
struct NonDeduced
{
typedef T type;
};
template <class T>
void f(const T& v, const std::function< bool( const typename NonDeduced<T>::type& ) >& validator );
As a side note, the syntax for the "deleted specialisation" you sketched is incorrect. It would be:
template<>
void f(const bool& v, const std::function< bool(const bool&) >& validator) = delete;
Upvotes: 1
Reputation: 5607
Although the Microsoft compiler gives a pretty misleading error, it is correct to reject your code.
The two parameter version does not get picked, because your lambda or function pointer can not be used to deduce the template argument of std::function
.
I would suggest making the function type a template parameter:
template< typename T, typename F >
void f(const T& v, F validator )
{
if (validator(v))
{
std::cout << v;
}
}
Clang is more clear about the errors:
main.cpp:7:6: note: candidate template ignored: could not match 'function<bool (const type-parameter-0-0 &)>' against '(lambda at main.cpp:38:10)'
void f(const T& v, const std::function< bool( const T& ) >& validator )
main.cpp:7:6: note: candidate template ignored: could not match 'function<bool (const type-parameter-0-0 &)>' against 'bool (*)(const int &)'
void f(const T& v, const std::function< bool( const T& ) >& validator )
Upvotes: 0