Home > Software design >  Template function with template argument parametrized over int
Template function with template argument parametrized over int

Time:12-28

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 ints. 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.

[Demo]

#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.

DEMO

#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);
}
  • Related