Sep 29, 2017

[C++][cppcon 2017] Write our own type trait

注意:
point of use (p.o.u) of template instantiation.

注意:
corner case.
i.e
there is no:
void&
void&&

void_t is a tool for mass production.
template<class...> using void_t = void;

善用declval
以下為何type是 Test&?
因為 'declval<Test>() = declval<Test>()' 是expression, 然後 lhs 是 l-value, 而 decltype(l-value) 為 l-value-ref.
decltype( declval<Test>() = declval<Test>() ); // type 為 Test&
i.e
Test &&t2 = Test{};
Test &&t3 = Test{};
t2 = t3;  // 傳回 Test&

Expression SFINAE

Use this formulation to trigger SFINAE:
decltype(void( declval<T>() = declval<U>() ))
or:
decltype( declval<T>() = declval<U>(), void() )
void_t<decltype( declval<T>() = declval<U>() )>

Any of these type-expressions will always evaluate to exactly void,
or else SFINAE away.
decltype(void(expression))
decltype(expression, void())
void_t<decltype(expression)>

Examples:
template<class T, class U, class> struct ISC_impl : false_type {};
template<class T, class U> struct ISC_impl<T, U, decltype(void(
static_cast<U>(declval<T>())
))> : true_type {};
template<class T, class U>
struct is_static_castable : ISC_impl<T, U, void> {};
template<class T, class> struct IP_impl : false_type {};
template<class T> struct IP_impl<T, decltype(
dynamic_cast<void*>(declval<remove_cv_t<T>*>())
)> : true_type {};
template<class T>
struct is_polymorphic : IP_impl<T, void*> {};
template<class T, class, class...> struct IC_impl : false_type {};
template<class T, class... Us> struct IC_impl<T, decltype(void(
::new (declval<void*>()) T(declval<Us>()...)
)), Us...> : true_type {};
template<class T, class... Us>
struct is_constructible : IC_impl<T, void, Us...> {};
template<class T, class, class...> struct INTC_impl : false_type {};
template<class T, class... Us> struct INTC_impl<T, decltype(void(
::new (declval<void*>()) T(declval<Us>()...)
)), Us...> : bool_constant<noexcept(
::new (declval<void*>()) T(declval<Us>()...)
)> {};
template<class T, class... Us>
struct is_nothrow_constructible : INTC_impl<T, void, Us...> {};
template<bool B, class T, class F>
struct conditional { using type = T; };
template<class T, class F>
struct conditional<false, T, F> { using type = F; };
template<bool B, class T, class F>
using conditional_t = typename conditional<B, T, F>::type;
template<bool B, class T, class F>
struct enable_if { using type = T; };
template<class T, class F>
struct enable_if <false, T, F> { using type = F; };
template<bool B, class T, class F>
using enable_if_t = typename enable_if <B, T, F>::type;
template<bool B, class T = void> struct enable_if { using type = T; };
template<class T> struct enable_if<false, T> {};
template<bool B, class T = void>
using enable_if_t = typename enable_if<B, T>::type;
template<bool B> using bool_if_t = enable_if_t<B, bool>;

SFINAE away a non-template function

1. Does this work?
template<class = enable_if_t<is_same_v<VoidPtr, void*>>>
fancy_poly_allocator() : mr_(get_default_resource()) {} // fancy_poly_allocator as constructor

The compiler will evaluate template default arguments eagerly whenever possible. So if
we want to delay the evaluation of the template argument, we have to put something in it
that depends on the p.o.u.
(對於default template parameter, compiler會最優先產生其type.
若無法,則compiler error out)

Thus:
template<bool B = is_same_v<VoidPtr, void*>, class =
enable_if_t<B>>
fancy_poly_allocator() : mr_(get_default_resource()) {}

even better:
template<class VoidPtr_ = VoidPtr,
class = enable_if_t<is_same_v<VoidPtr_, void*>>>

2.
Add a level of indirection

3.
To kick an overload out of your overload set,
put the ill-formed thing somewhere that affects the mangling / signature.
i.e
template<class U, bool_if_t<is_convertible_v<U*, T*>> = true>
offset_ptr(const offset_ptr<U>& rhs) : offset_ptr(rhs.ptr()) {};
template<class U, bool_if_t<is_static_castable_v<U*, T*>
&& !is_convertible_v<U*, T*>> = true>
explicit offset_ptr(const offset_ptr<U>& rhs) :
offset_ptr(static_cast<T *>(rhs.ptr())) {};

4.
Don’t lose sight of the meaning of your code.
Wrong due to the type can be deduced from the argument!
i.e
template<bool B = is_void_v<T>, class TR = enable_if_t<!B, T>&>
static auto pointer_to(TR r) {
return &r;
}

thus, let's put the SFINAE into parament type deduction:
i.e
template<bool B = is_void_v<T>>
static auto pointer_to(enable_if_t<!B, T>& r) {
return &r;
}

5.
Use if constexpr

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.