Шаблон для "AnySTLContainer <int>" С++

Я ищу способ предоставить функцию, которая принимает шаблон с шаблоном (STL), но требует, чтобы его элементы имели определенный тип (например, int).

Эти вызовы функций должны быть VALID:

std::vector<int> Argument;
void foo( Argument );

std::list<int> Argument
void foo( Argument );

std::deque<int> Argument
void foo( Argument );

...etc

Эти вызовы функций должны быть INVALID:

std::vector<float> Argument;
void foo( Argument );

std::list<double> Argument
void foo( Argument );

std::deque<char> Argument
void foo( Argument );

...etc

Есть ли способ шаблона "foo", так что контейнеры int принимаются, но контейнеры с разными типами элементов не принимаются?

Бест, Бен

Ответ 1

Вы можете просто использовать Sfinae, например:

#include <type_traits>
#include <utility>

template <typename Container>
typename std::enable_if< std::is_same< typename Container::value_type, int >::value,
void >::type foo(Container& c) {
  /* code here */
};

Который будет удален из набора перегрузок "foo", если в контейнере нет типа значения, который совпадает с "int". Вы также можете использовать другие черты, такие как is_convertible для более широкого использования родственных типов. Вызов foo с контейнером, который не имеет значений int, будет сообщен компилятором, не имеющим подходящих кандидатов перегрузки.

Если у вас нет поддержки на С++ 11, то вещи, которые я использовал выше, доступны в Boost как альтернатива С++ 98/03.

Ответ 2

Использовать семантику стандартной библиотеки:

  • Передайте пару итераторов в foo, а не в контейнер: это делает вашу функцию намного более общей
  • Используйте std::iterator_traits<Iterator>::value_type, чтобы перейти к типу значения
  • static_assert тип значения Iterator как int (или любой другой тип, который вы хотите)

Пример:

#include <list>
#include <vector>

template<typename Iterator>
void foo(Iterator begin, Iterator end)
{
    static_assert(std::is_same<int, typename std::iterator_traits<Iterator>::value_type>::value, 
                                "Invalid value type : must be int");
}

int main() {
    std::list<int> l1;
    std::vector<int> v1;

    foo(std::begin(l1), std::end(l1)); // OK
    foo(std::begin(v1), std::end(v1)); // OK

    std::vector<float> v2;
    foo(std::begin(v2), std::end(v2)); // Doesn't compile
}

демо-версия

Примечание:

  • Если foo требуется доступ к определенным функциям-членам контейнера (как отмечено Deduplicator, это может произойти по причинам производительности), вам может потребоваться придерживаться аргумента Container:

Пример: (обратите внимание на разницу для получения на value_type, как указано MooingDuck, это необходимо для работы с массивами):

template <typename Container>
void foo(const Container& c)
{

    static_assert(std::is_same<int, std::iterator_type<decltype(std::begin(c))>::value_type>::value, "Invalid value type : must be int");

   // Use c member function(s)
}

Ответ 3

Контейнеры STL имеют typedef value_type, поэтому вы можете использовать его.

Тогда вы можете запретить с помощью static_assert:

template <typename Container>
void foo(const Container& )
{
    static_assert(std::is_same<int, typename Container::value_type>::value, "expect int type");
}

или через SFINAE

template <typename Container>
typename std::enable_if<std::is_same<int, typename Container::value_type>::value>::type
foo(const Container& )
{
}

Ответ 4

Другое решение, использующее параметры шаблона шаблона

template<template<typename, typename...> class Container, typename... Params>
void foo(Container<int, Params...> const&)
{
  ...
}

Это будет соответствовать vector, list или deque, поскольку тип элементов в контейнере int.

Живая демонстрация

Ответ 5

Предполагая, что вам нужен итерируемый диапазон:

template<class I>
using value_type_t=typename std::iterator_traits<I>::value_type;

Устаревшее в С++ 14:

template<class T>using decay_t=typename std::decay<T>::type;
template<bool b,class T=void>using enable_if_t=typename std::enable_if<b,T>::type;

Тогда:

namespace adl_details{
  using std::begin;
 template<class C>using iterator_type=decay_t<decltype(begin(std::declval<C>()))>;
}
using adl_details::iterator_type;


template<class C>
enable_if_t<std::is_same<int, value_type_t<iterator_type_t<C>>>::value>
foo(C&& c){
}

имеет это свойство.