Specifying default value for template function argument - c++

Could you explain why the following code doesn't compile? An obvious workaround is to add a 1-argument overload of Apply, is there a simpler one?
template <typename T>
T Identity(const T& i_val)
{
return i_val;
}
template <typename Val, typename Fn>
Val Apply(const Val& v, Fn fn = Identity<Val>)
{
return fn(v);
}
int main() {
Apply(42); // error: no matching function for call to 'Apply(int)'
Apply(42, Identity<int>); // OK
return 0;
}

Template argument deduction doesn't work that way -- you can't deduce the type of an argument from a defaulted value. In C++11, you can however specify a default template argument:
template <typename Val, typename Fn = Val(&)(Val const &)>
Val Apply(const Val& v, Fn fn = Identity<Val>)
{
return fn(v);
}

Looking up the function to call consists of:
1. creating the set of candidates, which includes template argument deduction
2. determining the best overload
If I understand the standard correctly, only actual function arguments (i.e., not the default ones) take part in deducing the template arguments. Therefore from the argument 42, the only thing the compiler can infer is that Val = int. The overload does not enter the candidate set and the default argument is never looked at.

Apply is a templated function. You need to do Apply<MyValueType,MyFuncType>(42);
You could reasonably expect the compiler to infer Val to be int, but you can't expect it to infer the type of function, even though you've given a default parameter. As a result, it won't infer that you're trying to call that declaration of the Apply function.

Related

Why sometimes calling template function needs angle bracket and full type specification and sometimes not? [duplicate]

// Function declaration.
template <typename T1,
typename T2,
typename RT> RT max (T1 a, T2 b);
// Function call.
max <int,double,double> (4,4.2)
// Function call.
max <int> (4,4.2)
One case may be when you need to specify the return type.
Is there any other situation which requires the argument types to be specified manually?
(1) When there is no argument to the function and still it's a template type, then you may have to specify the arguments explicitly
template<typename T>
void foo ()
{}
Usage:
foo<int>();
foo<A>();
(2) You want to distinguish between value and reference.
template<typename T>
void foo(T obj)
{}
Usage:
int i = 2;
foo(i); // pass by value
foo<int&>(i); // pass by reference
(3) Need another type to be deduced instead of the natural type.
template<typename T>
void foo(T& obj)
{}
Usage:
foo<double>(d); // otherwise it would have been foo<int>
foo<Base&>(o); // otherwise it would have been foo<Derived&>
(4) Two different argument types are provided for a single template parameter
template<typename T>
void foo(T obj1, T obj2)
{}
Usage:
foo<double>(d,i); // Deduction finds both double and int for T
If the function template parameter appears in the function parameter list, then you don't need to specify the template parameters. For example,
template<typename T>
void f(const T &t) {}
Here T is a template parameter, and it appears in the function parameter list, i.e const T &t. So you don't need to specify the template parameter when calling this function:
f(10); //ok
Since the type of 10 is int, so the compiler can deduce the template parameter T from it, and T becomes int.
Note that since the type deduction is done from using the information of the function arguments, its called template argument deduction. Now read on.
If the template parameter doesn't appear in the function parameter list, then you have to provide the template parameter. Example:
template<typename T>
void g(const int &i) {}
Notice g() is different from f(). Now T doesn't appear in the function parameter list. So:
g(10); //error
g<double>(10); //ok
Note that if a function template templatizes on the return type as well, and the return type is different from the types appearing the function parameter list, then you've to provide the return type:
template<typename T>
T h(const T &t) {}
Since return type T is same as the function parameter, type deduction is possible from function argument:
h(10); //ok - too obvious now
But if you've this:
template<typename R, typename T>
R m(const T &t) {}
Then,
m(10); //error - only T can be deduced, not R
m<int>(10); //ok
Note that even though the function template m has templatized on two types : R and T, we've provided only ONE type when calling it. That is, we've written m<int>(10) as opposed to m<int,int>(10). There is no harm in writing the later, but its okay, if you don't. But sometimes you've to specif both, even if one type T can be deduced. It is when the order of type parameters is different as shown below:
template<typename T, typename R> //note the order : its swapped now!
R n(const T &t) {}
Now, you've to provide both types:
n(10); //error - R cannot be deduced!
n<int>(10); //error - R still cannot be deduced, since its the second argument!
n<int,int>(10); //ok
The new thing here is : the order of type parameters is also important.
Anyway, this covers only the elementary concept. Now I would suggest you to read some good book on templates, to learn all the advanced things regarding type deduction.
In general, you need to explicitly specify the types when the compiler can't figure it out on its own. As you mentioned, this often happens when the return type is templatized, since the return type cannot be inferred from the function call.
Template classes have the same problem -- instantiating a std::vector offers no way for the compiler to determine what type your vector is storing, so you need to specify std::vector<int> and so forth.
The type resolution is only performed in the case of function arguments, so it may be easier to view that as the special case; ordinarily, the compiler is unable to guess what type(s) to use.
The simple answer is that you need to provide the types when the compiler cannot deduce the types by itself, or when you want the template to be instantiated with a particular type that is different from what the compiler will deduce.
There are different circumstances when the compiler cannot deduce a type. Because type deduction is only applied to the arguments (as is the case with overload resolution) if the return type does not appear as an argument that is deducible, then you will have to specify it. But there are other circumstances when type deduction will not work:
template <typename R> R f(); // Return type is never deduced by itself
template <typename T>
T min( T const & lhs, T const & rhs );
min( 1, 2 ); // Return type is deducible from arguments
min( 1.0, 2 ); // T is not deducible (no perfect match)
min<double>( 1.0, 2 ); // Now it is ok: forced to be double
min<double>( 1, 2 ); // Compiler will deduce int, but we want double
template <typename T>
void print_ptr( T* p );
print_ptr<void>( 0 ); // 0 is a valid T* for any T, select manually one
template <typename T>
T min( T lhs, T rhs );
int a = 5, b = 7;
min<int&>(a,b)++; // Type deduction will drop & by default and call
// min<int>(a,b), force the type to be a reference
template <typename C>
typename C::value_type
min_value( typename C::const_iterator begin, typename C::const_iterator end );
std::vector<int> v;
min_value<std::vector<int> >( v.begin(), v.end() );
// Argument type is not deducible, there are
// potentially infinite C that match the constraints
// and the compiler would be forced to instantiate
// all
There are probably more reasons for an argument type cannot be deduced, you can take a look at §14.8.2.1 in the standard for the specifics of deduction of arguments from a function call.

Strange inconsistency between function template and "normal" function

I have the two functions that are almost the same (with the exception that one of them is a template):
int* bar(const std::variant<int*, std::tuple<float, double>>& t)
{
return std::get<0>(t);
}
template <typename... Args>
int* foo(const std::variant<int*, std::tuple<Args...>>& t)
{
return std::get<0>(t);
}
Than, they are use like this:
foo(nullptr);
bar(nullptr);
The second one compiles and returns (int*)nullptr, but the first one doesn't (in Visual Studio 2019 using C++17 giving the error foo: no matching overload found). Why? Why does making this function a template cause it to cease to compile?
Using foo like below doesn't help either, so the inability to deduce Args is probably not the problem:
foo<>(nullptr);
In contrary, the following does work:
foo(std::variant<int*, std::tuple<>>(nullptr));
Is it possible to somehow avoid the need to write this in such a long manner?
Apparently if a type of a function parameter depends on a template parameter that has to be deduced (because it's not specified in <...>), then implicit conversions don't apply when passing an argument to that parameter.
Source:
The function parameters that do not participate in template argument
deduction (e.g. if the corresponding template arguments are explicitly
specified) are subject to implicit conversions to the type of the
corresponding function parameter (as in the usual overload
resolution).
A template parameter pack that is explicitly specified may be extended
by template argument deduction if there are additional arguments:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
}
This also explains why foo<>(nullptr); still doesn't work. Since the compiler tries to deduce additional types to extend Args, in this case there doesn't seem to be any difference between foo(nullptr); and foo<>(nullptr);.
When a template function is considered it will only work for an exact match of the argument types at the call. This means that no conversions will be made (except for cv qualifiers).
A simple workaround in your case would be to make a function catch std::nullptr_t and forward that to your template.
int* foo(std::nullptr_t) {
return foo(std::variant<int*, std::tuple<>>{nullptr});
}
I would avoid this construct simply because the rules about exactly how the compiler will (if it even does it at all) resolve the overload are so confusing that I couldn't really tell you what it did without looking at a standards document, and code like that should be avoided. I would force the overload resolution you want this way instead:
template <typename... Args>
int *foo(const ::std::variant<int*, ::std::tuple<Args...>> &t)
{
return ::std::get<0>(t);
}
int *foo(int *ip)
{
using my_variant = ::std::variant<int *, ::std::tuple<>>;
return foo(my_variant{ip});
}
template <typename... Args>
int *foo(::std::tuple<Args...> const &t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{t});
}
template <typename... Args>
int *foo(::std::tuple<Args...> &&t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{::std::move(t)});
}

No matching function for call to find in templated linked list

Complier error "No matching function for call to" in my case a find function in a linked list.
Why this error?
Function
template<class T>
int find(std::shared_ptr<Node<T>> ptr, T val) {
int pos(0);
while (ptr) {
if (ptr->val == val)
return pos;
else {
ptr = ptr->next;
pos++;
}
}
return -1; // Not found
}
Invocation
std::cout << "Pos: " << find(myFloat, 9.83) << std::endl;
myFloat is a shared_ptr, and root node for a list of floats, successfully populated as follows:
Linked List of Floats
2.5 ⟹ 3.7 ⟹ 4.8 ⟹ 7.93 ⟹ 0.96 ⟹ 9.83 ⟹ 7.45
Struct
template<class T>
struct Node {
Node(T k):val(k){}
T val;
std::shared_ptr<Node<T>> next = nullptr;
};
Error
No matching function for call to 'find'
myFloat Definition
std::shared_ptr<Node<float>> myFloat = { std::make_shared<Node<float>>(0.) };
Since you didn't provide any explicit template arguments to find (as in find<float>(myFloat, 9.83)), the compiler must deduce the template argument T from the function arguments.
During template argument deduction, every appearance of a template parameter in the function parameter types is either in a deduced context or non-deduced context. In your case, both are considered deduced context. A type or value is determined for each appearance of a template parameter in a deduced context.
myFloat has type std::shared_ptr<Node<float>> and the first function parameter has type std::shared_ptr<Node<T>>, so T is deduced as float.
9.83 has type double, and the second function parameter has type T, so T is deduced as double.
Since template parameter T was deduced to be two different types, the overall deduction fails! When the same template parameter is deduced more than once, all the results must be identical.
So what can you do to make this just work?
An unpleasant solution would be to leave the template alone and require callers to use it carefully. find<float>(myFloat, 9.83) would work by explicitly providing the template argument. find(myFloat, 9.83f) would work by changing the type of the second function argument. But it's better to make the template more flexible and friendlier.
One simple fix is to just use two different template parameters.
template<class T, class U>
int find(std::shared_ptr<Node<T>> ptr, U val);
Then the function simply requires that the == operator works for the types T and U.
Or if you want the second function argument to be implicitly converted to the type used in the Node, you could intentionally turn the second use of T into a non-deduced context:
template <typename T>
struct identity {
using type = T;
};
template<class T>
int find(std::shared_ptr<Node<T>> ptr,
typename identity<T>::type val);
By appearing left of ::, the second T is now a non-deduced context, so the expression find(myFloat, 9.83) will deduce T from the first argument only, resulting in T=float, then substitute into the template function declaration. typename identity<float>::type works out to float, so there will be an implicit conversion from the double literal 9.83 to float to call the function.

C++: boost fusion fold with c++14 generic lambdas

I am trying to pass a generic lambda function to a boost::fusion::fold function so that I can iterate all the elements of a boost::fusion::vector. My goal is to call a non-const member function from each element in the vector. The problem is that even though the vector holds non-const values the type deduced by the generic lambda is a const reference. This results in my gcc-4.9.0 compiler (using CygWin) complaining that I am discarding the const qualifier.
#include <iostream>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/fold.hpp>
#include <boost/fusion/include/for_each.hpp>
class Silly {
public:
Silly(int x)
: x_(x){}
int increment(int i) {
return x_ += i;
}
private:
int x_;
};
using my_type = boost::fusion::vector<Silly, Silly>;
int main() {
my_type my_vector(1, 2);
boost::fusion::fold(my_vector, 0, [](int i, auto& x){return x.increment(i);}); //error: passing 'const Silly' as 'this' argument of 'int Silly::increment(int)' discards qualifiers
}
Now, if instead of the lambda I pass the following functor, the program compiles cleanly
struct functor {
template <class X>
int operator()(int i, X& x) {
return x.increment(i);
}
};
Is this a boost::fusion bug or am I missing something? Thanks in advance!
There are multiple boost::fusion::fold overloads. From boost's svn repo:
template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
Seq const
, State const
, F
>::type
BOOST_FUSION_FOLD_NAME(Seq const& seq, State const& state, F f)
{
return result_of::BOOST_FUSION_FOLD_NAME<Seq const,State const,F>::call(
state,
seq,
f);
}
template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
Seq
, State const
, F
>::type
BOOST_FUSION_FOLD_NAME(Seq& seq, State& state, F f)
{
return result_of::BOOST_FUSION_FOLD_NAME<Seq,State,F>::call(
state,
seq,
f);
}
template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
Seq const
, State const
, F
>::type
BOOST_FUSION_FOLD_NAME(Seq const& seq, State& state, F f)
{
return result_of::BOOST_FUSION_FOLD_NAME<Seq const,State,F>::call(
state,
seq,
f);
}
The compiler is allowed to instantiate the class template result_of::BOOST_FUSION_FOLD_NAME (*) in the return type for all these variants once type deduction and substitution have succeeded, before an overload is selected. In this case, the compiler must instantiate this class template in order to determine whether or not the return type is valid. If substitution (of the template arguments) in the return type leads to an invalid type in the immediate context, the overload is discarded. This is known as SFINAE.
(*) This name typically resolves to result_of::fold.
The instantiation of one of the overloads that has a Seq const& parameter tries now to determine the return type of the lambda. However, instantiating the lambda with a Silly const& second argument fails: increment cannot be called on a const object (this is what the compiler tells you).
If determining the return type fails, this should lead to a substitution failure in the fold overload we're trying to determine the return type of. However, substitution failures due to automatic return type deduction in lambdas and C++14 functions are not in the immediate context of the original template fold: They happen within the function that uses automatic return type deduction (here: the lambda).
A substitution failure not in the immediate context of the original template is a hard error, it is not a SFINAE-type error that you could recover from. (SFINAE = SFIICINAE)
If you explicitly specify the return type of the lambda, [](int i, auto& x) -> int {return x.increment(i);}, the function/lambda does not need to be instantiated to determine the return type. It can be determined from the declaration alone. Therefore, no substitution failure based on the return type happens for any of the overloads, and usual overload resolution can select an appropriate overload. The non-const Seq& overload is chosen, and the instantiation of the lambda will be valid.
Similarly, for the explicitly written functor: If the return type can be determined without instantiating the function, no error will occur. If you use C++14's return type deduction for ordinary functions, the same problem occurs:
struct functor {
template <class X>
auto operator()(int i, X& x) {
return x.increment(i);
}
};
As a side remark: As T.C. noted in a comment, a hard error also occurs for the following function object type:
struct functor {
int operator()(int i, Silly& x) {
return x.increment(i);
}
};
The reason this fails is different, though:
Again, all fold overloads need to instantiate the result_of::fold class template with their respective types. This class template however does not produce substitution errors in the immediate context: If the function passed cannot be called with the argument types passed, a hard error will occur.
Since a function of the type int(int, Silly&) cannot be called with arguments of the type int and Silly const&, a hard error occurs.
When writing the apply operator as a template (as in the example with C++14 return type deduction), the declaration of the operator() template can be instantiated for an second argument of type Silly const& (X will be deduced to be Silly const). The function definition cannot be instantiated, though, as this will result in the same error as in the OP: Silly::increment requires a non-const Silly object.
The instantiation of the definition of the function however happens only after overload resolution if there is no return type deduction. Therefore, this will not produce substitution failures.

In which cases one needs to specify the template's argument `types` specifically?

// Function declaration.
template <typename T1,
typename T2,
typename RT> RT max (T1 a, T2 b);
// Function call.
max <int,double,double> (4,4.2)
// Function call.
max <int> (4,4.2)
One case may be when you need to specify the return type.
Is there any other situation which requires the argument types to be specified manually?
(1) When there is no argument to the function and still it's a template type, then you may have to specify the arguments explicitly
template<typename T>
void foo ()
{}
Usage:
foo<int>();
foo<A>();
(2) You want to distinguish between value and reference.
template<typename T>
void foo(T obj)
{}
Usage:
int i = 2;
foo(i); // pass by value
foo<int&>(i); // pass by reference
(3) Need another type to be deduced instead of the natural type.
template<typename T>
void foo(T& obj)
{}
Usage:
foo<double>(d); // otherwise it would have been foo<int>
foo<Base&>(o); // otherwise it would have been foo<Derived&>
(4) Two different argument types are provided for a single template parameter
template<typename T>
void foo(T obj1, T obj2)
{}
Usage:
foo<double>(d,i); // Deduction finds both double and int for T
If the function template parameter appears in the function parameter list, then you don't need to specify the template parameters. For example,
template<typename T>
void f(const T &t) {}
Here T is a template parameter, and it appears in the function parameter list, i.e const T &t. So you don't need to specify the template parameter when calling this function:
f(10); //ok
Since the type of 10 is int, so the compiler can deduce the template parameter T from it, and T becomes int.
Note that since the type deduction is done from using the information of the function arguments, its called template argument deduction. Now read on.
If the template parameter doesn't appear in the function parameter list, then you have to provide the template parameter. Example:
template<typename T>
void g(const int &i) {}
Notice g() is different from f(). Now T doesn't appear in the function parameter list. So:
g(10); //error
g<double>(10); //ok
Note that if a function template templatizes on the return type as well, and the return type is different from the types appearing the function parameter list, then you've to provide the return type:
template<typename T>
T h(const T &t) {}
Since return type T is same as the function parameter, type deduction is possible from function argument:
h(10); //ok - too obvious now
But if you've this:
template<typename R, typename T>
R m(const T &t) {}
Then,
m(10); //error - only T can be deduced, not R
m<int>(10); //ok
Note that even though the function template m has templatized on two types : R and T, we've provided only ONE type when calling it. That is, we've written m<int>(10) as opposed to m<int,int>(10). There is no harm in writing the later, but its okay, if you don't. But sometimes you've to specif both, even if one type T can be deduced. It is when the order of type parameters is different as shown below:
template<typename T, typename R> //note the order : its swapped now!
R n(const T &t) {}
Now, you've to provide both types:
n(10); //error - R cannot be deduced!
n<int>(10); //error - R still cannot be deduced, since its the second argument!
n<int,int>(10); //ok
The new thing here is : the order of type parameters is also important.
Anyway, this covers only the elementary concept. Now I would suggest you to read some good book on templates, to learn all the advanced things regarding type deduction.
In general, you need to explicitly specify the types when the compiler can't figure it out on its own. As you mentioned, this often happens when the return type is templatized, since the return type cannot be inferred from the function call.
Template classes have the same problem -- instantiating a std::vector offers no way for the compiler to determine what type your vector is storing, so you need to specify std::vector<int> and so forth.
The type resolution is only performed in the case of function arguments, so it may be easier to view that as the special case; ordinarily, the compiler is unable to guess what type(s) to use.
The simple answer is that you need to provide the types when the compiler cannot deduce the types by itself, or when you want the template to be instantiated with a particular type that is different from what the compiler will deduce.
There are different circumstances when the compiler cannot deduce a type. Because type deduction is only applied to the arguments (as is the case with overload resolution) if the return type does not appear as an argument that is deducible, then you will have to specify it. But there are other circumstances when type deduction will not work:
template <typename R> R f(); // Return type is never deduced by itself
template <typename T>
T min( T const & lhs, T const & rhs );
min( 1, 2 ); // Return type is deducible from arguments
min( 1.0, 2 ); // T is not deducible (no perfect match)
min<double>( 1.0, 2 ); // Now it is ok: forced to be double
min<double>( 1, 2 ); // Compiler will deduce int, but we want double
template <typename T>
void print_ptr( T* p );
print_ptr<void>( 0 ); // 0 is a valid T* for any T, select manually one
template <typename T>
T min( T lhs, T rhs );
int a = 5, b = 7;
min<int&>(a,b)++; // Type deduction will drop & by default and call
// min<int>(a,b), force the type to be a reference
template <typename C>
typename C::value_type
min_value( typename C::const_iterator begin, typename C::const_iterator end );
std::vector<int> v;
min_value<std::vector<int> >( v.begin(), v.end() );
// Argument type is not deducible, there are
// potentially infinite C that match the constraints
// and the compiler would be forced to instantiate
// all
There are probably more reasons for an argument type cannot be deduced, you can take a look at §14.8.2.1 in the standard for the specifics of deduction of arguments from a function call.