Function Overloading, Could Not Deduce Template Argument - c++

I'm trying to overload a Sum function which accepts a [list or vector] start and end iterator as arguments. This compiler error is really confusing me. Relevant code is as follows:
template <typename T1, typename T2>
const double Sum(const typename T1::const_iterator& start_iter, const typename T2::const_iterator& end_iter)
{// overloaded function that calculates sum between two iterators
typename T1::const_iterator iterator_begin = start_iter;
typename T2::const_iterator iterator_end = end_iter;
double my_sum = 0;
for (iterator_begin; iterator_begin != iterator_end; iterator_begin++)
my_sum += *iterator_begin;
return my_sum;
}
int main()
{
list<double> test_list(10,5.1);
cout << Sum(test_list.begin(), test_list.end()); // compiler errors here
}
I get the following compiler errors:
iterators.cpp(72): error C2783: 'const double Sum(const
T1::const_iterator &,const T2::const_iterator &)' : could not deduce
template argument for 'T1'
iterators.cpp(72): error C2783: 'const double Sum(const
T1::const_iterator &,const T2::const_iterator &)' : could not deduce
template argument for 'T2'
iterators.cpp(72): error C2780: 'const double Sum(const
std::map &)' : expects 1 arguments - 2 provided
iterators.cpp(72): error C2780: 'const double Sum(const T &)' :
expects 1 arguments - 2 provided
How is the compiler not recognizing I'm trying to call the Sum function with two inputs? I'm calling the function incorrectly?
Thanks!

You dont need to tell it that iterators have to be members of some types T1 and T2, just template it on iterator type itself:
template <typename Iter>
const double Sum(Iter iterator_begin, Iter iterator_end)
{
double my_sum = 0;
for (; iterator_begin != iterator_end; ++iterator_end)
my_sum += *iterator_begin;
return my_sum;
}
int main()
{
std::list<double> test_list;
std::cout << Sum(test_list.begin(), test_list.end());
return 0;
}
also there is a standard std::accumulate that does this:
int main()
{
std::list<double> test_list;
std::cout << std::accumulate(test_list.begin(), test_list.end(), 0.0);
return 0;
}

First, I don't think you want to do this. Not all sequences have an
underlying container. (Think of istream_iterators, for example.) And
more importantly, you're distinctly allowing (and even encouraging)
begin and end iterators from different containers; there is no case
where you could legally use this function where T1 and T2 have
different types. The template should have a single parameter, which
should be an iterator; and by convention, the constraints on the
iterator should be expressed in the name of the parameter, e.g.
InputIterator (the case here), ForwardIterator, etc.
As to why your code doesn't compile:
In most cases, the types, templates, and non-type values that are used
to compose P participate in template argument deduction. That is, they
may be used to determine the value of a template argument, and the
value so determined must be consistent with the values determined
elsewhere. In certain contexts, however, the value does not
participate in type deduction, but instead uses the values of 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.
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a
qualified-id.
[...]
(From §14.8.2.5/4,5.)

Call method like this..
Sum<list<double>,list<double> >(test_list.begin(), test_list.begin());

Related

Passing a lambda function to a template method

I have the following templated method:
auto clusters = std::vector<std::pair<std::vector<long>, math::Vector3f>>
template<class T>
void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
{
}
And I have a function that looks like
auto comp1 = [&](
const std::pair<std::vector<long>, math::Vector3f>& n1,
const std::pair<std::vector<long>, math::Vector3f>& n2
) -> int {
return 0;
};
math::eraserFunction(clusters, comp1);
However, I get a syntax error saying:
116 | void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
| ^~~~~~~~~~~~~~
core.hpp:116:6: note: template argument deduction/substitution failed:
geom.cpp:593:23: note: 'math::method(const at::Tensor&, const at::Tensor&, int, float, int, int, float)::<lambda(const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&, const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&)>' is not derived from 'std::function<int(const T&, const T&)>'
593 | math::eraserFunction(clusters, comp1);
The function call tries to deduce T from both the first and second function parameter.
It will correctly deduce T from the first parameter, but fail to deduce it from the second parameter, because the second function argument is a lambda type, not a std::function type.
If deduction isn't possible from all parameters that are deduced context, deduction fails.
You don't really need deduction from the second parameter/argument here, since T should be fully determined by the first argument. So you can make the second parameter a non-deduced context, for example by using std::type_identity:
void eraserFunction(std::vector<T>& array, std::type_identity_t<std::function<int(const T&, const T&)>> func)
This requires C++20, but can be implemented easily in user code as well if you are limited to C++11:
template<typename T>
struct type_identity { using type = T; };
and then
void eraserFunction(std::vector<T>& array, typename type_identity<std::function<int(const T&, const T&)>>::type func)
std::identity_type_t<T> is a type alias for std::identity_type<T>::type. Everything left to the scope resolution operator :: is a non-deduced context, which is why that works.
If you don't have any particular reason to use std::function here, you can also just take any callable type as second template argument:
template<class T, class F>
void eraserFunction(std::vector<T>& array, F func)
This can be called with a lambda, function pointer, std::function, etc. as argument. If the argument is not callable with the expected types, it will cause an error on instantiation of the function body containing the call. You can use SFINAE or since C++20 a type constraint to enforce this already at overload resolution time.

C++ Candidate template ignored: deduced conflicting types for parameter 'T' ('std::__1::basic_string<char>' vs. 'char [4]')

I have the following code that fails when I call a bfs function and pass string literal as an argument:
template<typename T>
using Graph = unordered_map<T, vector<T> >;
template<typename T>
vector<T> bfs(const Graph<T>& graph, const T& root) {
...
}
int main() {
Graph<string> social_network = {
{"you", {"tony", "steve", "nick"}},
{"tony", {"clint"}},
{"nick", {"thor", "natasha"}},
{"steve", {"phil", "clint"}}
};
bfs(social_network, "you"); <-- the problem is here
}
The error is the following:
Candidate template ignored: deduced conflicting types for parameter 'T' ('std::__1::basic_string<char>' vs. 'char [4]')
if I explicitly specify string type for the argument, the code compiles successfully:
bfs(social_network, string("you"));
Is there a way to pass a string literal as an argument and make this code compile as well:
bfs(social_network, "you");
Thanks!
You can make type of root as being ignored in deduction of template parameters by using type_identity_t (required c++20), then T is taken from graph parameter:
const std::type_identity_t<T>& root
Demo

Error: "couldn't infer template argument '_Tp' " when passing in {} into

struct compar {
bool operator()(const vector<int>& a,
const vector<int>& b) const {
return a[1] < b[1];
}
};
...
auto it = lower_bound(events.begin(), events.end(), {0, events[i][0]}, compar());
This code gives me an error with the {0, events[i][0]}:
/bits/stl_algo.h:2022:5: note: candidate template ignored: couldn't infer template argument '_Tp'
lower_bound(_ForwardIterator __first, _ForwardIterator __last,
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/algorithmfwd.h:353:5: note: candidate function template not viable: requires 3 arguments, but 4 were provided
lower_bound(_FIter, _FIter, const _Tp&);
^
1 error generated.
But when I define it as a vector explicitly, it works as desired.
vector<int> point = {0, events[i][0]};
auto it = lower_bound(events.begin(), events.end(), point, compar());
Can someone explain why?
Braced-init-list has no type itself, it can't be used for deduction of template parameter. This is non-deduced context:
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.
The parameter P, whose A is a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or a reference to an array:
As you're showed, you have to specify the type explicitly, e.g.
auto it = lower_bound(events.begin(), events.end(), vector<int>{0, events[i][0]}, compar());
// ^^^^^^^^^^^
Or specify template arguments as
auto it = lower_bound<decltype(events.begin()), std::vector<int>>(events.begin(), events.end(), {0, events[i][0]}, compar());
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

c++ templates with template type deduction error

include
template <typename R, typename S, typename T>
T const min (R const& a, S const& b)
{
T val;
if( a > b )
{
val = static_cast<T>( b );
}
else
{
val = static_cast<T>( a );
}
return val;
}
// CANNOT change anything above this line --------------------
void B()
{
int val =0;
// Only change the next line
val = min (5,4.2);
assert( val == 4 );
}
when the code is compiled the following error is thrown
error C2783: 'const T min(const R &,const S &)' : could not deduce template argument for 'T'
Stuck trying to solve this.. . any help will be appreciated
The compiler error is telling you that it can't figure out what type T is supposed to be in your call to min, since you haven't specified it and the return type of a function or function template are not used during overload resolution or template argument deduction (unless it's a conversion operator, of course).
Since you can't change the definition of min (which is stupid) your only choice is to explicitly specify T in the call. However, since T is the last template parameter, you have to specify the two preceding template arguments too! Like this:
val = min<int, double, int>(5, 4.2);
The compiler cannot deduce the template argument if you don't use it.
That being said what you would do in this situation is specify in specify the template arguments when you call the function min. Just like this:
void B()
{
int val = 0;
// Only change the next line
val = min<int, double, int>(5, 4.2);
assert(val == 4);
}

Template function with dependent type parameters within template class

I've been trying to do this simple stuff and Visual studio 2008 does not seems to like it.
template <class CharType>
class SomeClass
{
public:
template <class T1, class T2>
static bool SomeOperator(const typename T1::const_iterator& p_Begin1,
const typename T1::const_iterator& p_End1,
const typename T2::const_iterator& p_Begin2,
const typename T2::const_iterator& p_End2)
{
// do some stuff..
}
};
And call it with something like this:
std::wstring a;
OtherString b;
SomeClass<wchar_t>::SomeOperator(a.begin(), a.end(), b.begin(), b.end());
What I get is compiler errors stating that it can't deduce template parameter T1 and T2
error C2783: 'bool SomeClass<CharType>::SomeOperator(const T1::const_iterator &,const T1::const_iterator &,const T2::const_iterator &,const T2::const_iterator &)' : could not deduce template argument for 'T1'
error C2783: 'bool SomeClass<CharType>::SomeOperator(const T1::const_iterator &,const T1::const_iterator &,const T2::const_iterator &,const T2::const_iterator &)' : could not deduce template argument for 'T2'
The compiler is simply unable to deduce types from this context.
Suppose std::wstring::const_iterator is actually const wchar_t*, which is likely. In that case, how does the compiler know it should substitute std::wstring rather than any other type T with T::const_iterator being const wchar_t* (perhaps vector<wchar_t>)?
It's impossible for the compiler to tell exactly. For similar reasons, you cannot deduce some_template<T>::type in function calls.
In your case, the workaround is easy. You don't actually need the container type - templating on the iterator types will work fine:
template <typename I1, typename I2>
static bool SomeOperator(const I1& p_Begin1, const I1& p_End1,
const I2& p_Begin2, const I2& p_End2)
{ /* stuff */ }
If you find yourself in a situation where you need the container type, you will have to either pass the container around or explicitly specify the type in the function call.
The compiler can't deduce that the iterator parameters are actually inner type defs of the containers providing them.
Use directyl two iterator types:
template< typename IT1, typename IT2>
static bool SomeOperator(const IT1& p_Begin1,
const IT1& p_End1,
const IT2& p_Begin2,
const IT2& p_End2)
{
// do some stuff..
}
You probably need to be explicit about the types for SomeOperator:
SomeClass<wchar_t>::SomeOperator<std::wstring, std::wstring>(a.begin(), a.end(), b.begin(), b.end());
I know that seems annoying, but it's actually fairly hard (and at times not possible) for the compiler to deduce the container type from a nested definition like T::const_iterator. Going from const_iterator back to T isn't straightforward AFAIK.