1001 *
*
Решение черт.
Обобщать не больше, чем нужно, и не меньше.
В некоторых случаях этого решения может быть недостаточно, поскольку оно будет соответствовать любому шаблону с такой подписью (например, shared_ptr
), и в этом случае вы можете использовать type_traits
, очень похоже на duck-typing (шаблоны, как правило, набираются уткой).
#include <type_traits>
// Helper to determine whether there's a const_iterator for T.
template<typename T>
struct has_const_iterator
{
private:
template<typename C> static char test(typename C::const_iterator*);
template<typename C> static int test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
// bar() is defined for Containers that define const_iterator as well
// as value_type.
template <typename Container>
typename std::enable_if<has_const_iterator<Container>::value,
void>::type
bar(const Container &c, typename Container::value_type const & t)
{
// Note: no extra check needed for value_type, the check comes for
// free in the function signature already.
}
template <typename T>
class DoesNotHaveConstIterator {};
#include <vector>
int main () {
std::vector<float> c;
bar (c, 1.2f);
DoesNotHaveConstIterator<float> b;
bar (b, 1.2f); // correctly fails to compile
}
Хороший шаблон обычно не искусственно ограничивает типы типов, для которых они допустимы (почему они должны?). Но представьте, что в приведенном выше примере вам нужен доступ к объектам const_iterator
, тогда вы можете использовать SFINAE и type_traits, чтобы наложить эти ограничения на вашу функцию.
Или просто как стандартная библиотека
Обобщать не больше, чем нужно, и не меньше.
template <typename Iter>
void bar (Iter it, Iter end) {
for (; it!=end; ++it) { /*...*/ }
}
#include <vector>
int main () {
std::vector<float> c;
bar (c.begin(), c.end());
}
Дополнительные примеры приведены в <algorithm>
.
Сила этого подхода заключается в его простоте и основана на таких понятиях, как ForwardIterator . Это даже будет работать для массивов. Если вы хотите сообщить об ошибках прямо в подписи, вы можете комбинировать это с чертами.
std
контейнеры с подписью типа std::vector
( не рекомендуется )
Самое простое решение уже аппроксимировано Kerrek SB, хотя оно недопустимо в C ++. Исправленный вариант выглядит так:
#include <memory> // for std::allocator
template <template <typename, typename> class Container,
typename Value,
typename Allocator=std::allocator<Value> >
void bar(const Container<Value, Allocator> & c, const Value & t)
{
//
}
Однако : это будет работать только для контейнеров, которые имеют ровно два аргумента типа шаблона, поэтому с треском провалится для std::map
(спасибо Люку Дантону).
Любые дополнительные аргументы шаблона ( не рекомендуется )
Исправлена версия для подсчета любого вторичного параметра:
#include <memory> // for std::allocator<>
template <template <typename, typename...> class Container,
typename Value,
typename... AddParams >
void bar(const Container<Value, AddParams...> & c, const Value & t)
{
//
}
template <typename T>
class OneParameterVector {};
#include <vector>
int main () {
OneParameterVector<float> b;
bar (b, 1.2f);
std::vector<float> c;
bar (c, 1.2f);
}
Однако : это все равно не удастся для контейнеров без шаблонов (спасибо Люку Дантону).