Как уже упоминал Geoffroy, вы можете использовать черту, чтобы определить, можно ли повторить T
. Затем вы используете это, чтобы выбрать правильную специализацию.
Итак, начните с черты is_iterable, показанной Jarod42 здесь :
// Code by Jarod42 (https://stackoverflow.com/a/29634934).
#include <iterator>
#include <type_traits>
namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;
template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});
template <typename T>
std::false_type is_iterable_impl(...);
}
template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));
Это дает вам is_iterable<T>
черта, которая наследуется от std::true_type
или std::false_type
. Теперь используйте это с SFINAE для создания двух специализаций:
template <class T, bool = is_iterable<T>::value>
class SimpleTemplate;
template <class T>
class SimpleTemplate<T, false> {
private:
T m_obj;
public:
SimpleTemplate (T obj) : m_obj(std::move(obj)) { }
void operator() () { m_obj.DoSomething(); }
};
template <class T>
class SimpleTemplate<T, true> {
private:
T m_collection;
public:
SimpleTemplate (T obj) : m_collection(std::move(obj)) { }
void operator() () {
for (auto && obj : m_collection) { obj.DoSomething(); }
}
};
Поскольку обе частичные специализации являются взаимоисключающими для любого заданного T
, вы не получите никаких ошибок в отношении неоднозначности.
Редактировать: Изменен 2-й аргумент шаблона в bool вместо класса. Это упрощает его полную специализацию в случае нежелательного поведения по умолчанию.
Например, для std::string
, для которого is_iterable
имеет значение true, просто выполните следующее. Обратите внимание, что я добавил конструкторы в SimpleTemplate, иначе я не смог получить полную специализацию для наследования конструктора базового класса.
template <>
class SimpleTemplate<std::string, true>
: public SimpleTemplate<std::string, false> {
// Inherit constructor.
using base = SimpleTemplate<std::string, false>;
using base::base;
};