Resolving ambiguous call in template argument deduction
-
21-12-2019 - |
Question
A typical function template signature:
template<typename Iterator, typename T>
T fn(Iterator first, Iterator last, T init)
{
T result;
// ...
return result;
}
The problem is, when I call it like this:
std::vector<long> data(1000,1);
fn(data.begin(), data.end(), 0);
or any other way without explicitly calling
fn<std::vector<long>::iterator, long>(data.begin(), data.end(),0);
then the type of T is an int
, and there is a risk of overflow and bad results in fn
.
So how do I specialize fn
so that the call to
fn(data.begin(), data.end(), 0);
is unambigous and results in T
to be set to Iterator::value_type
? The choice
template<Iterator>
typename iterator_traits<Iterator>::value_type fn(Iterator first, Iterator last, typename iterator_traits<Iterator>::value_type init)
{
//...
}
results in an ambiguous call error from g++/clang++.
EDIT:
I see my mistake now and the above code works with @Lightness Races suggestion below. Thanks for the help.
Solution
Why not simply pass a long?
fn(data.begin(), data.end(), 0L);
Beyond that you could probably do something like this:
#include <type_traits>
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type fn(
Iterator first,
Iterator last,
typename std::iterator_traits<Iterator>::value_type init
);
Live demo
I don't really see how you're at risk of an overflow, though.
OTHER TIPS
As:
0 is an int
0L is a long
You just have to call the method with the correct argument:
- Use directly the correct literal
as:
fn(data.begin(), data.end(), 0L);
- Or, you may use an intermediate variable
as:
long init = 0; // result in 0L
fn(data.begin(), data.end(), init);
- or you may cast the value:
as:
fn(data.begin(), data.end(), static_cast<long>(0));
One way to write fn
to avoid to call it correctly is
template<typename Iterator>
auto fn(Iterator first, Iterator last, typename std::decay<decltype(*first)>::type init)
-> typename std::decay<decltype(*first)>::type;