Простые шаблоны C++, подходящие для контейнеров STL

Мне нужен такой шаблон, который отлично работает

template <typename container> void mySuperTempalte (const container myCont)
{
    //do something here
}

тогда я хочу специализировать выше шаблон для std::string, поэтому я придумал

template <typename container> void mySuperTempalte (const container<std::string> myCont)
{
    //check type of container
    //do something here
}

который не работает, и выдает ошибку. Я хотел бы, чтобы второй пример работал, а затем, если это возможно, я хотел бы добавить некоторый код в шаблон, чтобы проверить, использовался ли std:: vector / std:: deque / std:: list, чтобы сделать что-то по-разному в каждом дело. Поэтому я использовал шаблоны, потому что 99% кода одинаковы как для векторов, так и для запросов и т. Д.

6 ответов

Решение

Если я правильно понимаю вашу проблему, у вас есть алгоритм, который будет работать для вектора контейнеров STL, deque и т. Д., Но вы пытаетесь написать шаблонную специализацию для строки. Если это так, то вы можете написать обобщенный шаблонный метод, который вы определили в своем вопросе:

template<typename container> void mySuperTempalte( const container &myCont )
{
    // Implement STL container code
}

Затем для вашей специализации строки вы объявляете:

template<> void mySuperTempalte( const container<std::string> &myCont )
{
    // Implement the string code
}

Для любой другой специализации просто измените объявление типа для myCont. Если вам действительно нужно сделать это для контейнеров vector и deque, тогда сделайте параметр шаблона параметром для типа в этом контейнере, а не самого контейнера, как предложил Sep.

template<typename C> void mySuperTempalte( const std::vector<C> &myCont)
{
    // check type of container
    // do something here
}

Стоит попытаться избежать этого, заставив вашу первую реализацию работать со всеми контейнерами STL, чтобы облегчить вашу жизнь, тогда вам нужна только специализация для строкового класса. Даже подумайте о преобразовании вашей строки в вектор, чтобы избежать специализации все вместе.

Кстати, я изменил параметр контейнера на константную ссылку, я предполагаю, что это то, что вам нужно, так как вы все равно объявляете объект const, таким образом вы избегаете копирования.

Вы пробовали параметр typename шаблона? Синтаксис немного странный, потому что он эмулирует синтаксис, используемый для объявления такого контейнера. Есть хорошая статья InformIT, объясняющая это более подробно.

template <template <typename> class Container>
void mySuperTemplate(Container<std::string> const& cont) {
}

Обратите внимание, что вы также должны объявить аргумент как ссылку!

Кстати: этот комментарий

//check type of container

мертвая распродажа, что вы делаете что-то не так. Вы не хотите проверять тип контейнера. Вместо этого пользователь более изощренно перегружен, как показано в ответе sep.

Специализироваться:

template<> void mySuperTempalte<std:string>(const std::string myCont)
{
    //check type of container
    //do something here
}

Чтобы специализироваться на векторе:

template<typename C> void mySuperTempalte (std::vector<C> myCont)
{
    //check type of container
    //do something here
}

Чтобы специализироваться на deque:

template<typename C> void mySuperTempalte (std::deque<C> myCont)
{
    //check type of container
    //do something here
}

Пока ответы кажутся полезными, но я думаю, что я бы использовал другую конструкцию. Я ожидаю, что все контейнеры будут определять value_type, так же как и контейнеры STL. Поэтому я могу написать

inline template <typename C> void mySuperTemplate (C const& myCont)
{
    mySuperTemplateImpl<C, typename C::value_type>(myCont);
}

В общем случае проще воздействовать на параметр, который вы извлекли явно.

@sep

"Простое" решение

Ответ, опубликованный 'sep', довольно хорош, вероятно, достаточно хорош для 99% разработчиков приложений, но может повторить некоторые улучшения, если он является частью интерфейса библиотеки:

Чтобы специализироваться на векторе:

template<typename C> void mySuperTempalte (std::vector<C> myCont)
{
    //check type of container
    //do something here
}

Это будет работать при условии, что вызывающая сторона не использует std::vector. Если это работает достаточно хорошо, чтобы специализироваться на векторах, списках и т. Д., Остановитесь здесь и просто используйте это.

Более полное решение

Во-первых, обратите внимание, что вы не можете частично специализировать шаблоны функций - вы можете создавать перегрузки. И если два или более из них совпадают в одинаковой степени, вы получите ошибки "неоднозначной перегрузки". Поэтому нам нужно сделать ровно одно совпадение в каждом случае, который вы хотите поддержать.

Одним из методов для этого является использование метода enable_if - enable_if позволяет вам выборочно удалять перегрузки шаблонов функций из списка возможных совпадений, используя неясное правило языка... в основном, если какое-либо логическое выражение имеет значение false, перегрузка становится "невидимой", Посмотрите SFINAE для получения дополнительной информации, если вам интересно.

Пример. Этот код может быть скомпилирован из командной строки с помощью MinGW (g++ parameterize.cpp) или VC9 (cl /EHsc parameterize.cpp) без ошибок:

#include <iostream>
#include <vector>
#include <string>
using namespace std;

template <bool B, class T> struct enable_if {};
template <class T> struct enable_if<true, T> { typedef T type; };

template <class T, class U> struct is_same { enum { value = false }; };
template <class T> struct is_same<T,T> { enum { value = true }; };

namespace detail{
    // our special function, not for strings
    //   use ... to make it the least-prefered overload
    template <class Container>
    void SpecialFunction_(const Container& c, ...){
        cout << "invoked SpecialFunction() default\n";
    }

    // our special function, first overload:
    template <class Container>
    // enable only if it is a container of mutable strings
    typename enable_if<
        is_same<typename Container::value_type, string>::value, 
        void
    >::type
    SpecialFunction_(const Container& c, void*){
        cout << "invoked SpecialFunction() for strings\n";
    }
}

// wrapper function
template <class Container>
void SpecialFunction(const Container& c){
    detail::SpecialFunction_(c, 0);
}

int main(){
    vector<int> vi;
    cout << "calling with vector<int>\n";
    SpecialFunction(vi);

    vector<string> vs;
    cout << "\ncalling with vector<string>\n";
    SpecialFunction(vs);
}

Выход:

d:\scratch>parameterize.exe calling
with vector<int> invoked
SpecialFunction() default

calling with vector<string> invoked
SpecialFunction() for strings

d:\scratch>

Является ли это хорошим дизайном или нет, оставлено для дальнейшего обсуждения. В любом случае, вы можете определить тип контейнера, используя частичные шаблоны специализаций. Особенно:

enum container_types
{
   unknown,
   list_container,
   vector_container
};

template <typename T>
struct detect_container_
{
   enum { type = unknown };
};

template <typename V>
struct detect_container_< std::vector<V> > // specialization
{
   enum { type = vector_container };
};

template <typename V>
struct detect_container_< std::list<V> >
{
   enum { type = list_container };
};

// Helper function to ease usage
template <typename T>
container_types detect_container( T const & )
{
   return static_cast<container_types>( detect_container_<T>::type );
}

int main()
{
   std::vector<int> v;

   assert( detect_container( v ) == vector_container );
}
Другие вопросы по тегам