Without using the features of C 11 and higher (I will accept them but would prefer C 98),
I have to write a template function with argument T which is an STL container of int
s. It receives such a container along with another int
which it tries to search for,
Right now I have this but it doesn't compile:
template <template<int> class T>
T::iterator easyfind(T &container, int val)
{
T::iterator it = container.begin();
for ( ; it != container.end(); it )
if (val == *it)
break ;
return (it);
}
I wonder if I can somehow force the T
parameter to always be a class template that is parametrized over integers... I tried writing T<int>
but it still doesn't compile.
CodePudding user response:
[NOTE] This answer uses C 20
. @PatrickRoberts made me notice that you were preferably requesting a C 98
solution. I leave it anyway because it may be of any help to you.
You can just add a requirement for your template, checking the container's type is int
.
#include <iostream> // cout
#include <list>
#include <type_traits> // is_same
#include <vector>
template <typename C>
requires std::is_same<typename C::value_type, int>::value
auto easyfind(const C& container, int val)
{
for (auto it{std::cbegin(container)}; it != std::cend(container); it)
{
if (val == *it) { return it; }
}
return std::cend(container);
}
int main()
{
std::vector<int> vi{1, 2, 3};
if (auto it{easyfind(vi, 2)}; it != std::cend(vi))
{
std::cout << *it << "\n";
}
std::list<int> li{4, 5, 6};
if (auto it{easyfind(li, 8)}; it != std::cend(li))
{
std::cout << *it << "\n";
}
std::vector<double> vd{0.5, 1.3, 2.8};
//if (auto it{easyfind(vd, 1.3)}; it != std::cend(vd)) // error
//{
// std::cout << *it << "\n";
//}
}
CodePudding user response:
Though there is an accepted answer, let me try to solve it using C 98
.
#include <vector>
#include <iostream>
namespace details{
struct true_type{static const bool value = true;};
struct false_type{static const bool value = false;};
template<typename T1,typename T2> struct is_same : false_type{};
template<typename T> struct is_same<T,T>:true_type{};
#define STATIC_ASSERT(expr, msg) \
{ \
char STATIC_ASSERT##msg[(expr)?1:-1]; \
}
};
template <class T>
typename T::iterator easyfind(T &container, int val)
{
using namespace details;
//static_assert can be used in C 11 onwards
STATIC_ASSERT((is_same<typename T::value_type,int>::value == true_type::value),InavalidType);
typename T::iterator it = container.begin();
for ( ; it != container.end(); it )
if (val == *it)
break ;
return (it);
}
int main(){
std::vector<int> a{1,2,3};
auto it = easyfind(a,1);
if(it != a.end())
std::cout<<*it<<std::endl;
auto it2 = easyfind(a,4);
if(it2 != a.end())
std::cout<<*it<<std::endl;
else
std::cout<<"Not Found"<<std::endl;
std::vector<double> b{1.0,2.0,3.0};
// std::vector<int>::iterator it3 = easyfind(b,1.0); //error
return 0;
}
CodePudding user response:
template <template<int> class T>
is not what you expect.
You want
template <template <typename> class Container>
typename Container<int>::iterator easyfind(Container<int> &container, int val)
{
#if 1 // Your code
typename Container<int>::iterator it = container.begin();
for ( ; it != container.end(); it )
if (val == *it)
break ;
return it;
#else // code with <algorithm>
return std::find(container.begin(), container.end(), val);
#endif
}
Unfortunately, std::vector
doesn't match Container
, as it has extra template parameter (Allocator, which is defaulted).
You might add overload:
template <template <typename, typename> class Container, typename Alloc>
typename Container<int, Alloc>::iterator easyfind(Container<int, Alloc> &container, int val)
C 11 would allow template <template <typename...> class Container
.
Simpler would be to use container directly as type:
template <typename Container>
#if 1 // No SFINAE
typename Container::iterator
#else // SFINAE with traits from C 11, which can be written trivially in C 98
typename std::enable_if<std::is_same<int, typename Container::value_type>>::type
#endif
easyfind(Container& container, int val)
{
#if 1 // Your code
typename Container::iterator it = container.begin();
for ( ; it != container.end(); it )
if (val == *it)
break ;
return it;
#else // code with <algorithm>
return std::find(container.begin(), container.end(), val);
#endif
}
but more generic would be to drop int
requirement completely:
template <typename Container>
typename Container::iterator
easyfind(Container& container, typename Container::const_reference val)
{
return std::find(container.begin(), container.end(), val);
}