Member check exists, possibly in the base class, version C ++ 11

Stack overflow.site/questions/5263 / ... provides a solution for statically checking for the existence of an element, possibly in a subclass of type:

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))); }; 

However, it does not work in C ++ 11 final classes, because it inherits from the tested class, which prevents final .

OTOH, this one:

 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 ) ; }; 

cannot find reserve(int/size_t) method reserve(int/size_t) in base classes.

Is there an implementation of this metafound that finds reserved() in the base classes of T and works if T is final ?

+27
c ++ c ++ 11 final typetraits template-meta-programming
Mar 02 2018-12-12T00:
source share
2 answers

In fact, everything has become much simpler in C ++ 11 thanks to decltype and late return bindings binding mechanisms.

Now it’s just easier to use methods to verify this:

 // 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; } 

Then you can use this in a class, for example:

 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) {} }; 

And you use it like this:

 template <typename T> bool reserve(T& t, size_t n) { Reserver<T, has_reserve_method(t)>::apply(t, n); return has_reserve_method(t); } 

Or you can choose the enable_if method:

 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; } 

Note that this switch is actually not that simple. In general, it is much simpler when only SFINAE exists - and you just want to enable_if one method and not provide any backup:

 template <typename T> auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) { t.reserve(n); } 

If substitution fails, this method is removed from the list of possible overloads.

Note: thanks to semantics , (comma operator) you can bind multiple expressions in decltype , and only the latter actually solves the type. It is convenient to check multiple operations.

+46
02 Mar. '12 at 10:00
source share

A version that also relies on decltype , but not on passing arbitrary types to (...) [this is not a problem at all, see Johannes comment]:

 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 {}; 

According to this attribute, I would like to indicate a type of type std::vector<int>& supports reserve : here expressions are checked, not types. The question this characteristic answers is β€œGiven the value of l lval for this type of T , these are the expressions lval.reserve(0); well formed.” Not identical to the question "Does this type or any of its basic types have a reserve member".

On the other hand, perhaps this feature! Remember that the new C ++ 11 feature is is_default_constructible , not has_default_constructor . The difference is subtle, but has its merits. (Finding the best finish name in the is_*ible on the left is an exercise.)

In any case, you can still use the trait, such as std::is_class , to achieve what you want.

+10
Mar 03 2018-12-12T00:
source share



All Articles