Проверка члена существует, возможно, в базовом классе, версия С++ 11
В qaru.site/info/5263/... предоставляется решение для статической проверки существования элемента, возможно, в подклассе типа:
template <typename Type>
class has_resize_method
{
class yes { char m;};
class no { yes m[2];};
struct BaseMixin
{
void resize(int){}
};
struct Base : public Type, public BaseMixin {};
template <typename T, T t> class Helper{};
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0);
static yes deduce(...);
public:
static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
};
Однако он не работает в классах С++ 11 final
, потому что он наследует от тестируемого класса, который предотвращает final
.
OTOH, этот:
template <typename C>
struct has_reserve_method {
private:
struct No {};
struct Yes { No no[2]; };
template <typename T, typename I, void(T::*)(I) > struct sfinae {};
template <typename T> static No check( ... );
template <typename T> static Yes check( sfinae<T,int, &T::reserve> * );
template <typename T> static Yes check( sfinae<T,size_t,&T::reserve> * );
public:
static const bool value = sizeof( check<C>(0) ) == sizeof( Yes ) ;
};
не удается найти метод reserve(int/size_t)
в базовых классах.
Есть ли реализация этого метафунта, который находит reserved()
в базовых классах T
и работает, если T
есть final
?
Ответы
Ответ 1
На самом деле все стало намного проще на С++ 11 благодаря механизмам привязки decltype
и late return bindings.
Теперь просто проще использовать методы для проверки этого:
// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
return true;
}
// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }
Затем вы можете использовать это в классе, например:
template <typename T, bool b>
struct Reserver {
static void apply(T& t, size_t n) { t.reserve(n); }
};
template <typename T>
struct Reserver <T, false> {
static void apply(T& t, size_t n) {}
};
И вы используете его так:
template <typename T>
bool reserve(T& t, size_t n) {
Reserver<T, has_reserve_method(t)>::apply(t, n);
return has_reserve_method(t);
}
Или вы можете выбрать способ enable_if
:
template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
t.reserve(n);
return true;
}
template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
return false;
}
Обратите внимание, что это переключение на самом деле не так просто. В общем, это намного проще, когда существует только SFINAE - и вы просто хотите enable_if
один метод и не предоставлять никакого резервного:
template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
t.reserve(n);
}
Если сбой подстановки, этот метод удаляется из списка возможных перегрузок.
Примечание: благодаря семантике ,
(оператор запятой) вы можете связать несколько выражений в decltype
, и только последний фактически решает тип. Удобно проверять несколько операций.
Ответ 2
Версия, которая также полагается на decltype
, но не на передачу произвольных типов на (...)
[это вообще не проблема, см. комментарий Johannes]:
template<typename> struct Void { typedef void type; };
template<typename T, typename Sfinae = void>
struct has_reserve: std::false_type {};
template<typename T>
struct has_reserve<
T
, typename Void<
decltype( std::declval<T&>().reserve(0) )
>::type
>: std::true_type {};
Я хотел бы указать в соответствии с этим признаком тип типа std::vector<int>&
поддерживает reserve
: здесь выражения проверяются, а не типы. Вопрос, на который отвечает эта характеристика, - "Учитывая значение l lval
для такого типа T
, это выражения lval.reserve(0);
хорошо сформированы". Не идентичен вопросу "Имеет ли этот тип или какой-либо из его базовых типов член reserve
".
С другой стороны, возможно, эта особенность! Помните, что новый признак С++ 11 имеет стиль is_default_constructible
, а не has_default_constructor
. Различие тонкое, но имеет свои достоинства. (Поиск лучшего имени финиша в стиле is_*ible
слева как упражнение.)
В любом случае вы все равно можете использовать черту, например std::is_class
, чтобы достичь того, чего хотите.