C++11 std equivalent of Boost has_dereference

徘徊边缘 提交于 2019-12-07 18:06:59

问题


Many of Boost's SFINAE helpers have appeared in the std library with C++11, but has_dereference doesn't seem to have. Other than this feature, I've managed to eliminate a Boost dependency from my package, and I'd like to get rid of it entirely, so how best to get the same effect using just C++11 std features?


回答1:


The easiest way to check if a class has some function with no external dependencies is generally with the void_t idiom.

// Define this once in your project somewhere accessible
template <class ... T>
using void_t = void;

The trick then is always the same; you define a class template that inherits from std::false_type:

template <class T, class = void>
struct has_dereference : std::false_type {};

This is the "fallback" class template. Now we're going to define a specialization that only works when the type has the property we want:

template <class T>
struct has_dereference<T, void_t<decltype(*std::declval<T>())>> : std::true_type {};

To use, just do:

bool x = has_dereference<int*>::value;
bool y = has_dereference<int>::value;

etc.

I will add that technically, operator* is actually a family of functions; the operator can be both CV qualified and value category qualified. Whenever you perform detection on a type, you are actually doing detection within this family. I won't get more into details because it's rarely encountered in practice (operator* is rarely value category qualified, and the operator almost always has a const version, and volatile rarely comes up) but it's worth being aware of in case you see something surprising.

This technique is worth knowing about, especially if you are doing meta-programming without dependencies like Boost or Hana. You can read more about void_t here: How does `void_t` work.




回答2:


This is a neat little SFINAE trait writing helper. It uses std::void_t, which you can reimplement if you lack it.

namespace details {
  template<template<class...>class Z, class v, class...Ts>
  struct can_apply:std::false_type{};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=typename details::can_apply<Z, void, Ts...>::type;

Once you have that, your problem is easy.

template<class T>
using deref_result = decltype(*std::declval<T>());

template<class T>
using can_deref = can_apply<deref_result, T>;

The idea here is to hide the std::void_t machinery. You write a trait that expresses "result of some computation", and from that we can get "is that computation valid".

A highly portable void_t looks like:

namespace details {
  template<class...>struct voider{using type=void;};
}
template<class...Ts>
using void_t=typename voider<Ts...>::type;

doing it in one line breaks some older compilers, and the 2 line version is easy enough, might as well.




回答3:


A modified version of Yakk's:

template <class...> struct pack {};

namespace detail {
    template<template <class...> class Z, class Pack, class = void>
    struct can_apply_impl : std::false_type {};

    template<template<class...>class Z, class...Ts>
    struct can_apply_impl<Z, pack<Ts...>, std::void_t<Z<Ts...>> > : std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = detail::can_apply_impl<Z, pack<Ts...>>;

DEMO



来源:https://stackoverflow.com/questions/36583961/c11-std-equivalent-of-boost-has-dereference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!