Шаблон для "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){
}
имеет это свойство.