I am new in using C++ templates.
I need to write a template function specialization for my project.
It is a simple Sum function for different type inputs and it calculates the sum between two iterators. The original function is generic and so accepts a template argument. The template specialization is written for Maps.
#include <map>
#include <string>
template <typename T>
double Sum(T &it_beg, T &it_end) {
double sum_all = 0;
for(it_beg++; it_beg != it_end; it_beg++)
sum_all += *it_beg;
return sum_all;
};
template <>
double Sum(std::map<std::string, double> &it_beg, std::map<std::string, double> &it_end) {
double sum_all = 0;
for(it_beg++; it_beg != it_end; it_beg++)
sum_all += it_beg->second;
return sum_all;
};
when I try to run the code, I get the following errors
...\sum.h(21): error C2676: binary '++' : 'std::map<_Kty,_Ty>' does not define this operator or a conversion to a type acceptable to the predefined operator
1> with
1> [
1> _Kty=std::string,
1> _Ty=double
1> ]
I appreciate if anyone could give me a hint !
thanks
Your function signature should look like this (possibly without references) so you can pass in rvalues (iterators are cheap to copy anyway):
template <>
double Sum(std::map<std::string, double>::iterator it_beg,
std::map<std::string, double>::iterator it_end)
std::map does not define operator++, clearly your arguments are meant to be std::map::iterators.
Don't forget to remove the references from the main template function parameters too.
There's also this:
for(it_beg++; it_beg != it_end; it_beg++)
Why are you incrementing it_beg as you enter the loop? You can leave initialization statement empty.
Related
I want to pass a lambda to a function, but I have run into a problem of successfully passing it onto the function. The function chooses to append TrueVal or FalseVal and creates a vector of boolean, based on the given condition.
I'm using 2019 Visual Studio's ISO C++14 Standard to compile the code.
#include <iostream>
#include <vector>
using namespace std;
template<typename T, typename T1, typename T2>
vector<bool> ConstructNestedVectorByElements(T condition, T1 TrueVal, T2 FalseVal) {
vector<bool> TempCol;
TempCol = {};
for (int i = 0; i < 3; i++)
{
if (condition(i)) {
TempCol.emplace_back(TrueVal);
}
else {
TempCol.emplace_back(FalseVal);
}
}
return TempCol;
}
int main()
{
vector<int> NumList = { 0, 1, 2 };
vector<bool> BoolList = {true, false, true};
auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, false); //this works
auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement); //error
return 0;
};
The error message:
C2440 'initializing': cannot convert from 'T2' to 'bool' ...\include\vector line 2385
1>...\vector(2385,18): error C2440: 'initializing': cannot convert from 'T2' to 'bool'
1> with
1> [
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
1>...\vector(2385,18): message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>...\Source.cpp(19): message : see reference to function template instantiation 'decltype(auto) std::vector<bool,std::allocator<bool>>::emplace_back<T2&>(T2 &)' being compiled
1> with
1> [
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
1>...\Source.cpp(36): message : see reference to function template instantiation 'std::vector<std::vector<bool,std::allocator<bool>>,std::allocator<std::vector<bool,std::allocator<bool>>>> ConstructNestedVectorByElements<main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,bool,main::<lambda_e116e485fb739b952327b9205614af81>>(T,T1,T2)' being compiled
1> with
1> [
1> T=main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,
1> T1=bool,
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
template <class... _Valty>
decltype(auto) emplace_back(_Valty&&... _Val) {
bool _Tmp(_STD forward<_Valty>(_Val)...);
push_back(_Tmp);
I think the problem might be one of the following:
I'm passing more than one type of argument into T2 (a lambda and a bool): Perhaps I used the wrong keyword, typename, to initialize T2? I tried with class but the same thing occurred.
OriginalElement isn't given parameters when it requires them: This confuses me a bit. If I change the line to:
TempCol.emplace_back(FalseVal(i, j)); //this is line 19
This error shows up:
C2064 term does not evaluate to a function taking 2 arguments ...\Source.cpp line 19
However, this seems not to be the case for condition(i, j), which compiles correctly. Is there a difference in handling (what I assume to be) boolean when in a conditional, and when appending it to a vector?
Lambdas not being constexpr, so it can't be used in templates: I don't really understand it, but there seems to be some relationship with this topic: (1, 2, 3)
The issue is that OriginalElement is not a bool and cannot be implicitly converted to one. You can call it to get a bool by passing an int. Change this line in the template:
TempCol.emplace_back(FalseVal(i));
then
auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement);
Compiles fine, but passing false will not work, because false is not a callable. T2 cannot be both, a callable and a bool. If you want both instantiations to work, you can change the first to
auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, [](int){ return false;});
Complete Demo
However, I suggest you to write two overloads. One that takes a callable. And to avoid the overhead of the callable, another one that takes a plain bool (no template parameter, let the conversion happen on the caller).
The following is cute little template that I use often. Simply tells me if the given element is a member of a collection (which itself must be compatible with the find_if template):
// returns true if a given collection contains the given element
// NOTE: This is NOT optimized for associative containers!
template <typename ELEMENT, typename COLLECTION, typename PREDICATE>
bool contains(const COLLECTION & collection, ELEMENT element, PREDICATE predicate)
{
return collection.end() != std::find_if(collection.begin(), collection.end(), boost::bind(predicate, element, _1));
}
I'm finding that VC2012 balks if I try to use a lambda as the predicate:
if (!contains(specs, str, [] (CString pathname, CString pattern) { return AsBool(PathMatchSpec(pathname, pattern)); }))
continue;
VS2012SP1 spits out the following for the above context:
1>c:\users\steve\projects\cimex cad-cam\15.0\3rd party\boost\boost\bind\bind.hpp(69): error C2039: 'result_type' : is not a member of 'CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>'
1> c:\users\steve\projects\cimex cad-cam\15.0\cimex application\cimcad\macro directory.cpp(166) : see declaration of 'CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>'
1> c:\users\steve\projects\cimex cad-cam\15.0\3rd party\boost\boost\bind\bind_template.hpp(15) : see reference to class template instantiation 'boost::_bi::result_traits<R,F>' being compiled
1> with
1> [
1> R=boost::_bi::unspecified,
1> F=CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>
1> ]
1> c:\users\steve\projects\cimex cad-cam\15.0\mfc toolbox\miscellaneous.h(360) : see reference to class template instantiation 'boost::_bi::bind_t<R,F,L>' being compiled
1> with
1> [
1> R=boost::_bi::unspecified,
1> F=CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>,
1> L=boost::_bi::list2<boost::_bi::value<CString>,boost::arg<1>>
1> ]
1> c:\users\steve\projects\cimex cad-cam\15.0\cimex application\cimcad\macro directory.cpp(166) : see reference to function template instantiation 'bool contains<CString,substring_container_adapter,CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>>(const COLLECTION &,ELEMENT,PREDICATE)' being compiled
1> with
1> [
1> COLLECTION=substring_container_adapter,
1> ELEMENT=CString,
1> PREDICATE=CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>
1> ]
I'm unclear on how to coerce things to accept the predicate lambda. Seems that boost is unable to deduce the return type of the lambda. And I'm unclear on what I can do to fix that?
I could define a local std::binary_function derivative functor. Just seems like it would be better to fix contains<> to allow it to handle lambdas directly.
It seems to be an issue with boost::bind. Using std::bind instead, your code builds fine with lambdas in VS2012:
#include <functional>
#include <algorithm>
#include <vector>
template <typename ELEMENT, typename COLLECTION, typename PREDICATE>
bool contains(const COLLECTION & collection, ELEMENT element, PREDICATE predicate)
{
return collection.end() != std::find_if(collection.begin(), collection.end(), std::bind(predicate, element, std::placeholders::_1));
}
std::vector<int> a;
int main()
{
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_back(42);
bool c = contains(a, 42, [](int a, int b) { return a == b; });
return 0;
}
The same code builds just fine with g++ as well.
You could always try using another lambda:
template <typename ELEMENT, typename COLLECTION, typename PREDICATE>
bool contains(const COLLECTION & collection, ELEMENT element, PREDICATE predicate)
{
typedef typename COLLECTION::value_type VALUE;
return collection.end() != std::find_if(collection.begin(), collection.end(),
[&]( VALUE const & e ){ return predicate( element, e ); });
}
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());
Compilation error in VS2010:
c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(1840): error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const triangle' (or there is no acceptable conversion)
h:\kingston_backup\ocv\ocv\delaunay.h(281): could be 'triangle &triangle::operator =(const triangle &)'
while trying to match the argument list '(const triangle, const triangle)'
c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(1853) : see reference to function template instantiation '_FwdIt std::_Remove_if,_Pr>(_FwdIt,_FwdIt,_Pr)' being compiled
with
[
_FwdIt=std::_Tree_unchecked_const_iterator,std::allocator,true>>>,
_Mytree=std::_Tree_val,std::allocator,true>>,
_Pr=triangleIsCompleted
]
h:\kingston_backup\ocv\ocv\delaunay.cpp(272) : see reference to function template instantiation '_FwdIt std::remove_if,triangleIsCompleted>(_FwdIt,_FwdIt,_Pr)' being compiled
with
[
_FwdIt=std::_Tree_const_iterator,std::allocator,true>>>,
_Mytree=std::_Tree_val,std::allocator,true>>,
_Pr=triangleIsCompleted
]
I think the problem is in passing the arguments to the remove_if() of the STL, as suggested by the compiler error. I have added the following comment to the line:
//**** ERROR LINE
class triangleIsCompleted
{
public:
triangleIsCompleted(cvIterator itVertex, triangleSet& output, const vertex SuperTriangle[3])
: m_itVertex(itVertex)
, m_Output(output)
, m_pSuperTriangle(SuperTriangle)
{}
bool operator()(const triangle& tri) const
{
bool b = tri.IsLeftOf(m_itVertex);
if (b)
{
triangleHasVertex thv(m_pSuperTriangle);
if (! thv(tri)) m_Output.insert(tri);
}
return b;
}
};
// ...
triangleSet workset;
workset.insert(triangle(vSuper));
for (itVertex = vertices.begin(); itVertex != vertices.end(); itVertex++)
{
tIterator itEnd = remove_if(workset.begin(), workset.end(), triangleIsCompleted(itVertex, output, vSuper)); //**** ERROR LINE
// ...
}
remove_if does not remove anything (in the sense of erasing). It copies values around, so that all remaining values end up at the beginning of the range (and the rest of the range is in a more or less unspecified state).
Since keys in an associative container are immutable, it is not possible to copy values from one place to another within a set, so remove_if can't work for it.
The standard library does not seem to contain remove_if for set, so you'd have to roll your own. It might look like this:
#include <set>
template <class Key, class Compare, class Alloc, class Func>
void erase_if(std::set<Key, Compare, Alloc>& set, Func f)
{
for (typename std::set<Key, Compare, Alloc>::iterator it = set.begin(); it != set.end(); ) {
if (f(*it)) {
set.erase(it++); //increment before passing to erase, because after the call it would be invalidated
}
else {
++it;
}
}
}
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.