I'm trying to create a templated function with an optional argument and I'm having trouble understanding why the compile fails. This is my test (contrived) code:
#include <iostream>
#include <vector>
template <class UnaryPredicate>
int GetCountIf(std::vector<int> v, UnaryPredicate pred = [](auto) { return true; }) {
int count=0;
for (auto i: v) {
if (pred(i)) {
count++;
}
}
return count;
}
int main() {
auto v = std::vector<int>{0, 1, 2, 3, 4, 5};
std::cout << "NumOddElements=" << GetCountIf(v, [](auto val) { return (val % 2 == 1); }) << '\n';
// std::cout << "NumElements=" << GetCountIf(v) << '\n';
}
The code compiles only if I call GetCountIf()
with both arguments. If I try to pass it only 1 argument, compilation fails with this error:
main.cpp:18:34: error: no matching function for call to 'GetCountIf'
std::cout << "NumElements=" << GetCountIf(v) << '\n'; ^~~~~~~~~~ main.cpp:5:5: note: candidate template ignored: couldn't infer template argument 'UnaryPredicate' int GetCountIf(std::vector v, UnaryPredicate pred = { return true; }) { ^ 1 error generated.
When the compiler comes across a call to GetCountIf
with only 1 argument, why is it not able to deduce that the type of the optional lambda? If I explicitly specify the type of the predicate like this, it works:
template <typename T, class UnaryPredicate = std::function<bool(T)>>
int GetCountIf(std::vector<T> v, UnaryPredicate pred = [](T) { return true;}) {
...
}
Why does this work?
(I'm using C++14)
A function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.
To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>( float original ); Template arguments may be omitted when the compiler can infer them.
A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.
Templates Specialization is defined as a mechanism that allows any programmer to use types as parameters for a class or a function. A function/class defined using the template is called a generic function/class, and the ability to use and create generic functions/classes is one of the critical features of C++.
Note that the default value of function parameter won't be used for template argument deduction of template parameter; which leads to template argument deduction failure, the type of UnaryPredicate
can't be deduced.
See non-deduced contexts.
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:
template<typename T, typename F> void f(const std::vector<T>& v, const F& comp = std::less<T>()); std::vector<std::string> v(3); f(v); // P1 = const std::vector<T>&, A1 = std::vector<std::string> lvalue // P1/A1 deduced T = std::string // P2 = const F&, A2 = std::less<std::string> rvalue // P2 is non-deduced context for F (template parameter) used in the // parameter type (const F&) of the function parameter comp, // that has a default argument that is being used in the call f(v)
and
Type template parameter cannot be deduced from the type of a function default argument:
template<typename T> void f(T = 5, T = 7); void g() { f(1); // OK: calls f<int>(1, 7) f(); // error: cannot deduce T f<int>(); // OK: calls f<int>(5, 7) }
On the other hand, if you specify a default value std::function<bool(T)>
for template parameter UnaryPredicate
, then it'll be used as the type for UnaryPredicate
if the argument for UnaryPredicate
is not explicitly specified or not be deduced.
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